一、表膨胀查询与处理
1、创建扩展
create extension pgstattuple;
2、表膨胀查询
pgstattuple提供了pgstatetuple()和pgstatindex()两个统计表和索引的方法,较系统表pg_class的表统计信息,pgstatetuple()还统计了表中的dead tuples。
如下查询出来表的怕膨胀系数为81%。
select *, 1.0 - tuple_len::numeric/table_len as bloat from pgstattuple('tab_brin1');
select * from pg_relpages('tab_brin1');
占用2414个page。
3、表膨胀处理
vacuum (verbose,full,analyze) tab_brin1;
回收完后,膨胀系数降到3%。
select *, 1.0 - tuple_len::numeric table_len as bloat from pgstattuple('tab_brin1');
select * from pg_relpages('tab_brin1');
表占用473个page。
二、数据库防止事务回卷
1、
当前事务只能看到比表上xmin事务号小的记录,txid(事务id)的最大值为32位,即2^32为4294967296(约40亿),当数据库的事务号到达最大值后事务号就用尽了,此时需要重新使用,又从3(0、1、2为保留的事务id,后面会讲)开始。
这就会导致任何原来表上的数据的xmin均大于当前事务号,造成看不到以前的数据现象,这就违背了mvcc的原则。当然postgresql数据库系统不会让这种情况发生,当数据库的年龄到达20亿(后面会讲为什么是20亿)时就要采取措施了,数据库中的表就需要清理事务号(使用vacuum freeze),以此来降低数据库表的年龄。降低数据库的年龄是autovacuum 进程在表的年龄到达阀值后自动进行的,也可以vacuum freeze命令手动执行。autovacuum 操作也有可能会进行部分行freeze而不是全表freeze。
2、VacuumFreeze
为了保证同一个数据库中的最新和最旧的两个事务之间的年龄不超过2^31,postgresql引入了冻结(freeze)功能。
涉及到的术语:
表年龄:当前事务号距上一次执行freeze操作的事务id的差值。
元组年龄:当前元组的xmin距上一次执行freeze操作的事务id的差值。
如果发生当新老事务id差超过21亿的时候,事务号会发生回卷,此时数据库会报出如下错误并且拒绝接受所有连接,必须进入单用户模式执行vacuumfreeze操作。
事务冻结操作:vacuum freeze tab_brin1;
查看指定表的年龄:
SELECT relname, age(relfrozenxid) as xid_age,pg_size_pretty(pg_table_size(oid)) as table_size FROM pg_class WHERE relname = 'tab_brin1';
查询所有数据库的年龄:
select datname, age(datfrozenxid) from pg_database;
3、参数设置
在postgresql中,vacuum是一个比较耗费io的过程,而vacuumfreeze更是被称为“冻结炸弹”,因为涉及到了大量的读写io,读io(datafile)和写io(datafile以及写wal)。对于业务繁忙的库,可能会出现如下情况:
可能有很多大表的年龄会先后到达2亿,数据库的autovacuum会开始对这些表依次进行vacuumfreeze,从而集中式的爆发大量的读写io,数据库和操作系统响应迟缓,如果又碰上业务高峰,会出现很不好的影响。
所以设置好参数尤为重要:
- 设置vacuum_cost_delay为一个比较高的数值(例如50ms),这样可以减少普通vacuum对正常数据查询的影响。
- autovacuum_freeze_max_age和vacuum_freeze_table_age的值也不适合设置过大,因为过大会造成pg_clog中的日志文件堆积,来不及清理。我们把autovacuum_freeze_max_age设置为最大值20亿。
- vacuum_freeze_table_age设置为0.95* autovacuum_freeze_max_age。
- vacuum_freeze_min_age不宜设置过小,比如我们freeze某个元组后,这个元组马上又被更新,那么之前的freeze操作其实是无用功,freeze真正应该针对的是那些长时间不被更新的元组。
- 生产环境中做好pg_database.frozenxid的监控,当快达到触发值时,我们应该选择一个业务低峰期窗口主动执行vacuumfreeze操作,而不是等待数据库被动触发。
- 分区,把大表分成小表。每个表的数据量取决于系统的io能力,前面说了vacuumfreeze是扫全表的,现代的硬件每个表建议不超过32gb,单表数据不要超过3000w。
- 对大表设置不同的vacuum年龄
- 用户自己调度 freeze,如在业务低谷的时间窗口,对年龄较大,数据量较大的表进行vacuumfreeze。
- 年龄只能降到系统存在的最早的长事务即 min(pg_stat_activity.(backend_xid,backend_xmin))。因此也需要密切关注长事务。
知识分享,需人人参与,看完请点赞留言,共同讨论进步