POSTGRESQL 1000行数据的表,110G的存储没有dead tuple 引出的问题

news/2024/7/9 19:51:05 标签: postgresql, 数据库

df644eca848f5234495d3d174c7f5f00.png

开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(接近700人左右 1 + 2)

群里有一个同学问了一个问题,没有死元祖,表的行数1000行,存储容量110G

b81fc89f05612034b0d3aa2f5ac604bc.png

我们来完成这个小目标

17c3e354a89dc580e1c95d3845dd6021.png

PG 13 版本,然后我们灌入1000万的数据

94363421758996c57bb12a8d37783e93.png

0f2ed0d32974e7972412b5deb6c5ac27.png

然后我们,添加索引,删除数据99%,剩下1000行,然后在进行vacuum 的操作,最后看这表,还是 1710MB .提问同学的“ 1 个亿的小目标”达到了,也就是说我们还原的,那位客官所提到的部分问题。

71327363e5d52fda69fd536bf4b99363.png

然后我们继续往里面灌入数据,只是插入,不进行任何的UPDATE 操作,然后我们不断查看表空间的扩展情况, 并未如我们所愿插入的数据使用了原有我们认为可以利用的数据清理后的空间。

9ace4b9cc651b4a8f49d37e9eaf7fbad.png

771575bdf51e644beb961872a938d29a.png

9d9309460f22a028638e15212b157079.png

那么提出的问题是,在什么状态下,PG 在数据的操作中,会出现无法利用原有空间的情况。

1   原始插入的数据小,UPDATE 后的数据大,并且填充因子设置上并未考虑此问题的情况下,会产生此问题。但基于PG 支持TOAST 功能,所以在处理这个问题方面上,超大的字段,并不会引起页面无法重复利用的问题。

8b41d59193af98c2592a2e603f1c0516.png

我们创建一个表,并且在表中塞入大型的数据

test=# create table test (id int,name text);

CREATE TABLE

test=# SELECT lp,lp_off,t_ctid FROM heap_page_items(get_raw_page('test',0));

ERROR:  block number 0 is out of range for relation "test"

test=# insert into test (id,name) values (1,'677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888679999999999997777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888677777788888888888888888888888888888888888');

768ea8d0751dc2122144d27f39c352cb.png

从上面看,我们的数据占用了两个页面不到的样子,那么现在我们针对 ID  1  2 的数据进行UPDATE,并且每个更新了2次 但是在UPDATE 中的数据量明显比之前插入的数据量要大。

在我们进行UPDATE 后,整体的分布变化了

4edd10ad33735fe38e4e811ef35a14f9.png

我们在连接插入了新的数据后,相关的第一个页面的空出的位置被重复了利用。

ec6a36aaf01fe37ea40db3151cc613f7.png

那么我们在删除 1  2号记录后,我们在重新插入 1  2 号记录

8bf48bc0e68080972d4e8e7b0368b119.png

数据分别在第一页和第二页插入,说明删除的数据空间并不能马上被重复利用。而我们继续插入数据,则此时,发现空出的空间不能被使用,即使你做了vacuum 的工作,新的数据已经创建了新的页面。

873cb89cda8113a2a9cc10bcc0a0a02a.png

为什么在删除了数据,并且进行了VACUUM 还不能使用那些空间,还要占据新的空间?在重新对表进行vacumm full后,整体

395229a6ea5c9590638604cc70e3b0a2.png

这里关于无法利用数据页面空间的主要常规解释有如下的部分:这里简单的在重复一下

1   长事务

SELECT pid, datname, usename, state, backend_xmin, backend_xid

FROM pg_stat_activity

WHERE backend_xmin IS NOT NULL OR backend_xid IS NOT NULL

ORDER BY greatest(age(backend_xmin), age(backend_xid)) DESC;

2   逻辑复制槽,或自建的复制槽在使用中接收端失效或出现问题的

SELECT slot_name, slot_type, database, xmin

FROM pg_replication_slots

ORDER BY age(xmin) DESC;

3   在PG中没有提交事务,或者OPT的,如何查询IOPT的方式在下方

SELECT gid, prepared, owner, database, transaction AS xmin

FROM pg_prepared_xacts

ORDER BY age(transaction) DESC;

4  主从库中从库开启了hot_standby_feedback=on 的情况  为了减少复制冲突,可以在备用服务器上设置hot_standby_feedback = on。然后备用服务器将把最旧的打开事务告知主服务器,主服务器上的VACUUM将不会删除备用服务器上仍然需要的旧行版本。

SELECT application_name, client_addr, backend_xmin

FROM pg_stat_replication

ORDER BY age(backend_xmin) DESC;

除此以外,还有什么情况下是会产生PG 的表虽然被 VACUUM 了,但是还是空间重复利用率低。

1  delete 的数据,比插入的数据占用的空间小,导致新插入的数据无法放置到已经空出的空间中,只能新开空间。当然处理这类问题,可以对表的填充因子进行变化,但是问题是实际上这是背着抱着的问题,我一开始设置的填充率低,则后期UPDATE ,DELETE 后,新的数据库可以进入的可能性高,但是一开始你因为设置的填充率的问题,会导致一开始占用的磁盘空间高,所以是背着抱着的问题。但是还是建议针对经常UPDATE 的表,DELETE 的表,请将你的填充率可以降低到 85%  80%

2  BUG 问题

大部分的问题都与 old_snapshot_threshold 参数有关,这个参数开启后,有运行 select ... for update 将所有的 vacuum操作都卡死的情况(在PG 13)

以及在早期的版本中开启后,无法在删除表中大量数据后,还无法进行磁盘空间归还给操作系统的问题。所以我们建议不要针对old_snapshot_threshold 参数进行设置,默认就好。

996eea9759c1d31b375a097fc3118e65.png

Thread: BUG #17196: old_snapshot_threshold is not honored if there is a transaction : Postgres Professional

Re: [HACKERS] Document that vacuum can't truncate if old_snapshot_threshold >= 0 (mail-archive.com)

b12c29de37d0d48f1882be6068d6ceda.png

https://github.com/EnterpriseDB/zheap/blob/master/src/backend/access/table/vacuumblk.c

具体上面的代码可以从上面的连接找到。

另外在我测试中,使用了PG14.7 的版本,在打开了参数后,并设置了超过1分钟的配置后,曾发生过一次,删除数据,VACUUM 中明明发现有死的元组,但无法进行回收的问题。(没有在操作中通过事务,霸占这个表的行,导致VACUUM 无法回收,只是就打开这个参数,然后删除数据,然后就无法回收),但后面在操作就无法复现了。(所以把这个归为一个未知不确定的问题)

最后,删除数据无法空间回收,还是可以通过heap_page_items 函数来分析你整体最后页面中是否有数据,如果有,那么空间是无法释放给系统的。


https://dba.stackexchange.com/questions/320395/significant-size-differences-between-size-calculations-for-tables-in-postgresq

另外此次的学习中,还发现了一个较好的分析表磁盘空间利用的语句,可以从上面的网址获得,但是不建议在大表和生产繁忙的系统中运行,有可能有性能风险。

de9730ef9c3692d3bfe1f9fd8e3950a5.png

44fe39170130aa8a311aca78ab096e2a.png


http://www.niftyadmin.cn/n/184961.html

相关文章

数据库补充笔记2

Oracle 命令分类操作: 分类 命令 DDL create:创建;drop:删除;alter:修改;rename: 重命名; truncate:截断 DML insert:插入;delete:删除&…

MySQL 查询 JSON字段匹配

直接干货 MySQL从5.7版本开始,引入了JSON类型字段,这使我们可以在MySQL数据库中存储JSON格式的数据,并保留其对象格式,再也不用转成字符串类型保存了,减少了许多字符串类型和对象类型之间的转换步骤。而同时也会衍生出…

GameFramework框架详解之 Sound声音管理

一.前言 虽然现在大厂的大项目可以选择想Wwise这类的音频插件来进行声音的开发,不需要什么声音模块管理了,但是我们也知道,小公司或者小项目组,是用不起这种收费软件的,所以大部分的项目还是需要我们自己去管理音频的。 我在很多框架中都见过Sound音频模块的实现,自己也…

什么是modbus通信协议?

Modbus是一种用于工业通信的通信协议,最初由Modicon在1979年推出,现在已经成为工业自动化领域中最流行的协议之一。Modbus通常用于控制和监视自动化设备,如PLC、传感器、计量器等。本篇博客将介绍Modbus的基本原理和应用,并介绍如…

单例模式C++用法示例

二.单例模式一.前言1.为什么需要单例模式2.实现方法二.懒汉模式——在需要使用对象时才进行实例化,而不是在程序启动时就进行实例化1.优缺点2.适用场景3.C程序示例二.饿汉模式——在程序启动时就进行实例化,而不是在需要使用对象时才进行实例化1.优缺点2…

Jenkins通用配置

记安装Jenkins后需要进行的通用配置。 一、设置成中文 1、先在插件管理中安装以下两个插件 Locale plugin 和 Localization: Chinese (Simplified) 1)点击【Manage Jenkins】->【Manage Plugins】选项。 2)点击【Avaliable】选项,右侧搜…

大数据采集技术之AST解释说明

AST解释说明。 You: AST是什么 ChatGPT: AST代表抽象语法树(Abstract Syntax Tree),它是一种编程语言的中间表示形式,用于在编译器和解释器中处理和分析程序代码。 在编译器中,源代码被分析并转换成AST,AST是一种表示代码结构的树形数据结构,其中每个节点表示代码的…

var let区别

在JavaScript中,var和let都是用于声明变量的关键字。但是,它们在作用域、变量提升、重复声明等方面有所不同。 下面是var和let的主要区别: 作用域:var声明的变量具有函数作用域或全局作用域,而let声明的变量具有块级…