摘要: 在数据库中用于保护数据库磁盘空间的工具是 VACUUM,其重要的作用是删除那些曾经标示为删除的数据并开释空间。
vacuum 的性能
回收空间
数据库总是一直地在执行删除,更新等操作。良好的空间治理十分重要,可能对性能带来大幅提高。
执行 delete 操作后,表中的记录只是被标示为删除状态,并没有开释空间,在当前的 update 或 insert 操作中该局部的空间是不可能被重用的。
在数据库中用于保护数据库磁盘空间的工具是 VACUUM,其重要的作用是删除那些曾经标示为删除的数据并开释空间。通过 vacuum 清理后,空间能力失去开释。
VACUUM 回收已删除元组占据的存储空间。在个别的数据库操作里,那些曾经 DELETE 的元组或者被 UPDATE 过后过期的元组是没有从它们所属的表中物理删除的;在实现 VACUUM 之前它们依然存在。因而咱们有必须周期地运行 VACUUM,特地是在常更新的表上。
解冻 tuple 的 xid
在每条记录(tuple)的 header 中,寄存 xmin,xmax 信息 (增删改事务 ID)。transactionID 的最大值为 2 的 32 次,即无符整形来示意。当 transactionID 超过此最大值后,会循环应用。
这会带来一个问题:就是最新事务的 transactionID 会小于老事务的 transactionID。如果这种状况产生后,就没有方法按 transactionID 来辨别事务的先后,也没有方法实现 MVCC 了。
因而用 vacuum 后盾过程,按肯定的周期和算法触发 vacuum 动作,将过老的 tuple 的 header 中的事务 ID 进行解冻。解冻事务 ID,行将事务 ID 设置为“2”(“0”示意有效事务 ID;“1”示意 bootstrap,即初始化;“3”示意最小的事务 ID)。被解冻的事务 ID 比任何事务都要老。这样就不会呈现下面的这种状况了。
更新 visibility map
在数据库中,有一个 visibility map 用来标记那些 page 中是没有 dead tuple 的。这有两个益处,一是当 vacuum 进行 scan 时,间接能够跳过这些 page。二是进行 index-only scan 时,能够先查看下 visibility map。这样缩小 fetch tuple 时的可见性判断,从而缩小 IO 操作,进步性能。另外 visibility map 绝对整个 relation,还是小很多,能够 cache 到内存中。
vacuum full 与 vacuum 的区别
1.vacuum 只是将删除状态的空间开释掉,转换到可能从新应用的状态,然而对于零碎来说该数据块的闲暇空间并没有反馈到零碎的元数据中,并不进行空间合并。
而 vacuum full 本质上是重建了整个表,以达到空间合并的成果。
2.vacuum 执行过程中对表加 4 级锁,不会影响表的增删改查,而 vacuum full 对表加 8 级锁,执行过程中表无法访问。
3.vacuum 对列存表有效
vacuum full 的执行流程
建长期表
数据库会新建一个长期表,长期表继承老表所有属性。
这个阶段会对 pg_class 申请“RowExclusiveLock”锁,因为须要插入记录。
拷贝数据
将原来的数据 copy 到 temp 表中。
对长期表,老表以及索引都以“AccessExclusiveLock”模式关上。
另外对于 toast,只是 lock,不关上。
在这个过程中实现 Dead Tuple 的清理。
表替换
新表将老表替换掉。会对 pg_class 再次申请“RowExclusiveLock”锁。
重建索引
是在替换之后实现的,重建索引时,会更新一些统计信息。对表申请“ShareLock”锁。
删除长期表
索引重建实现后,将带有老物理文件的新长期表进行删除。
vacuum full 执行慢的常见场景
1. 存在锁争抢
在 cn 上执行 select * from pg_stat_activity where query like ‘%vacuum%’; 找到 vacuum full 的 pid
查看该线程的期待状态,如果期待状态是 acquire lock,阐明存在锁期待
select * from pg_thread_wait_status where tid = 139878309295872;
在 pg_locks 中查问 vacuum full 在等哪个锁
select * from pg_locks where pid = 139878309295872 and granted = ‘f’;
查看持有该锁的线程
select * from pg_locks where relation = 544793 and granted = ‘t’;
查看该线程对应的语句
select query from pg_stat_activity where pid = 139877539612416;
依据语句判断是否能够杀掉该语句持续做 vacuum full,或者另外找工夫窗做 vacuum full
2. 存在 IO/ 网络问题导致事务无奈提交
执行一个简略的 create table 语句,如果 create table 语句执行也很慢,阐明存在 IO/ 网络问题,进一步排查 IO 和网络
3. 零碎表过大导致 vacuum full 慢
vacuum full 任意一张表时,都会扫描 pg_class、pg_partition、pg_proc 三张零碎表,当这三个零碎表过大时,也会导致 vacuum full 较慢
能够在排除 IO/ 网络问题(即 create table 语句不慢)后,对空表做 vacuum full,察看执行速度,如果空表做 vacuum full 也比较慢,则阐明就是这三张零碎表较大导致 vacuum full 任意表都慢
4. 排除以上场景之后,能够查看表定义中是否应用了 PCK
当存在 PCK 时,表做 vacuum full 时会进行全排序,此时如果表较大或 psort_work_mem 设置较小,就会导致 PCK 排序时产生下盘,进行外排,效率急剧下降。
能够通过调大 psort_work_mem 进行躲避
点击关注,第一工夫理解华为云陈腐技术~