1 问题
最近发现一个奇怪的问题,再使用pg_dump备份一个库时候,发现备份后的大小只有几个G大小,但是统计了整个数据库的大小,发现居然超过了1000G。刚开始还以为备份过程中中断出现问题了,重新备份一次大小还是一样。后来发现是出现了膨胀非常厉害的表导致的。
2 解决
2.1 膨胀表的产生
首先需要从pg的MVCC机制说起,MVCC即多版本并发控制,也就是说,在PostgreSQL中,一次行的update和delete不会立即删除旧的版本,而是被标记为删除,一旦被标记删除的行数非常多后,统计信息发现表中预期的行数和实际的行数相差很大,表的膨胀程度就很大。但是最后,任何事务都不会再对一个过时或者被删除的行版本感兴趣,而被标记为删除的行所占的空间必须被收回来用于新行,这样可以避免磁盘空间需求无限制增长,这通过运行VACUUM命令来完成。
2.2 膨胀表的维护
vacuum的标准形式移除表和索引中的死亡行版本并将空间标记为未来可以重用,不过,它并不会将该空间交给操作系统,除非在一些特殊的情况下,表的尾部的一个或多个页面变成完全空闲并且能够很容易地得到一个排他表锁。
vacuum full通过把死亡空间之外的内容写成一个完整的新版本表文件来主动紧缩表,这将最小化表的尺寸,但是同时花费的时间也是巨大的。它也需要额外的磁盘空间来用于表的新副本创建,直到操作完成,完成期间,这张表会加上排它锁,任何事务都会被block掉。
这两种命令到底什么时候用呢?这里其实建议例行清理的一般目标是多做标准的vacuum来避免需要vacuum full。自动清理守护进程和能尝试这样的工作,但是永远也不会进行vacuum full。在这种情况下,其思想是不让表保持他们的最小尺寸,而是保持磁盘空间使用的稳定状态:每个表占用的空间等于其最小尺寸加上清理被标记为删除的空间。如果该表将来预期还会再次增长这样就没有意义,因此,对于维护频繁被更新的表,周期性运行标准的vacuum比少量运行vacuum full更好。
2.3 查看膨胀表
greenplum中每一个库下面都有一个gp_toolkit模式,里面有一个gp_bloat_diag的视图,其查询的是系统表中该库中膨胀表信息。
各个字段的意义如下:
Table 1. gp_bloat_diag view
Column | Description |
---|---|
bdirelid | Table object id. |
bdinspname | Schema name. |
bdirelname | Table name. |
bdirelpages | Actual number of pages on disk. |
bdiexppages | Expected number of pages given the table data. |
bdidiag Bloat | diagnostic message.diagnosis of bloat (ratio from 1 to 3 -> no bloat, ratio from 4 to 10 -> moderate bloat, ratio > 10 -> significant bloat) |
查看有哪些表膨胀比较厉害,建议使用排序的方式,这样可以查看到哪些表的relpage比较大,而expect page比较少的情况。
select * from gp_toolkit.gp_bloat_diag order by bdirelpages desc, bdidiag
上述命令执行后,得到了如下的结果
bdirelid | bdinspname | bdirelname | bdirelpages | bdiexppages | bdidiag Bloat |
---|---|---|---|---|---|
1249 | pg_catalog | pg_attribute | 1599965 | 8 | significant amount of bloat suspected |
67535346 | ad | ad_big_table | 399419 | 10246 | significant amount of bloat suspected |
1259 | pg_catalog | pg_class | 123130 | 8 | significant amount of bloat suspected |
5094 | pg_catalog | gp_relation_node | 77010 | 8 | significant amount of bloat suspected |
1247 | pg_catalog | pg_type | 46211 | 8 | significant amount of bloat suspected |
2608 | pg_catalog | pg_depend | 20316 | 22 | significant amount of bloat suspected |
可以很明显看到,pg_catalog下的几张表膨胀非常厉害,都属于严重膨胀表了,可能vacuum都不能解决,需要立马运行vacuum full命令。
统计表的信息时候也提示如下:
ANALYZE pg_catalog.pg_attribute
NOTICE: ANALYZE detected all empty sample pages for table pg_attribute, please run VACUUM FULL for accurate estimation.
下面查看一下排在第一的那张表pg_attribute大小。
select pg_size_pretty(pg_relation_size('pg_attribute'));
pg_size_pretty
--------------
440 GB
那么问题找到了,就是系统表占用了大量的库空间,pg_attribute存储的是有关表列的信息,数据库中每一个表的每一个列都恰好在pg_attribute中有一行,这其中也会有索引的属性项,并且事实上,所有具有pg_class项的对象在这里都有属性项。
2.4 解决办法
找一个业务不太繁忙的时间,对上述膨胀非常厉害的表进行vacuum full操作,收缩表空间实际占用的大小。同时开始每天定时查询膨胀表进行vacuum操作,维持表占用空间的相对稳定。
补充:经过将近14个小时的表压缩,目前该表已经正常,从falcon监控可以看到,每一台虚拟机减少的空间大小都大概在200G,说明该表已经成功被缩减空间了,后期需要脚本定期执行,维持表占用空间的稳定。
select pg_size_pretty(pg_relation_size('pg_attribute'));
pg_size_pretty
--------------
34 MB
该脚本可以参考 https://github.com/YICHAO1/scripts/blob/master/bash/vacuumgp .sh