目录
一 问题
二 解决
2.1 为何会膨胀
2.2vauum 可以解决表膨胀吗?
2.2.1 vacuum 和vacuum full有什么区别呢?
2.3查看表膨胀
2.4 解决
2.5 优化
同事反馈一个sql执行长达1000s,通过分析问题,发现该表严重膨胀,70W的数据量表大小达到18GB。同时还发现该表的分布键设计不合理。此表有频繁的TP业务。
select pg_size_pretty(pg_relation_size('ds_value_list_st'));
分布情况
在PostgreSQL中,一次行的update和delete不会立即删除旧的版本,而是被标记为删除,一旦被标记删除的行数非常多后,统计信息发现表中预期的行数和实际的行数相差很大,表的膨胀程度就很大。但是最后,任何事务都不会再对一个过时或者被删除的行版本感兴趣,而被标记为删除的行所占的空间必须被收回来用于新行,这样可以避免磁盘空间需求无限制增长,这通过运行VACUUM命令来完成。
vacuum的标准形式移除表和索引中的死亡行版本并将空间标记为未来可以重用,不过,它并不会将该空间释放,除非在一些特殊的情况下,表的尾部的一个或多个页面变成完全空闲并且能够很容易地得到一个排他表锁。
vacuum full通过把死亡空间之外的内容写成一个完整的新版本表文件来主动紧缩表,这将最小化表的尺寸,但是同时花费的时间也是巨大的。它也需要额外的磁盘空间来用于表的新副本创建,直到操作完成,完成期间,这张表会加上排它锁,任何事务都会被block掉。vacuum full命令实际上重建了整张表和上面的索引,可以使用社区提供的pg_repack工具。它的原理是基于原表创建一张新表,同时利用触发器,将原表上当前的增量更新不断记录下来。新表建好后,将所记录的增量更新应用到新表,直到新旧表数据完全一致。最后将新旧表表名互换,删除旧表,即完成了表的空间整理操作,回收了空间
这两种命令到底什么时候用呢?这里其实建议例行清理的一般目标是多做标准的vacuum来避免需要vacuum full。自动清理守护进程和能尝试这样的工作,但是永远也不会进行vacuum full。在这种情况下,其思想是不让表保持他们的最小尺寸,而是保持磁盘空间使用的稳定状态:每个表占用的空间等于其最小尺寸加上清理被标记为删除的空间。如果该表将来预期还会再次增长这样就没有意义,因此,对于维护频繁被更新的表,周期性运行标准的vacuum比少量运行vacuum full更好。
|
无vacuum |
VACUUM |
VACUUM FULL |
删除大量数据之后 |
只是将删除数据的状态置为已删除,该空间不能记录被重新使用。
|
如果删除的记录位于表的末端,其所占用的空间将会被物理释放并归还操作系统。如果不是末端数据,该命令会将指定表或索引中被删除数据所占用空间重新置为可用状态, 那么在今后有新数据插入时,将优先使用该空间,直到所有被重用的空间用完时,再考虑使用新增的磁盘页面。 |
不论被删除的数据是否处于数据表的末端,这些数据所占用的空间都将被物理的释放并归还于操作系统。之后再有新数据插入时,将分配新的磁盘页面以供使用。 |
执行效率 |
|
由于只是状态置为操作,因此效率较高。 |
在当前版本的PostgreSQL(v9.1)中,该命令会为指定的表或索引重新生成一个数据文件,并将原有文件中可用的数据导入到新文件中,之后再删除原来的数据文件。因此在导入过程中,要求当前磁盘有更多的空间可用于此操作。由此可见,该命令的执行效率相对较低。 |
被删除的数据所占用的物理空间是否被重新规划给操作系统。 |
不会 |
不会 |
会 |
在执行VACUUM命令时,是否可以并发执行针对该表的其他操作。 |
|
由于该操作是共享锁,因此可以与其他操作并行进行。 |
由于该操作需要在指定的表上应用排它锁,因此在执行该操作期间,任何基于该表的操作都将被挂起,知道该操作完成。 |
推荐使用方式 执行后其它操作的效率 |
在进行数据清空时,可以使用truncate操作,因为该操作将会物理的清空数据表,并将其所占用的空间直接归还于操作系统。 对于查询而言,由于存在大量的磁盘页面碎片,因此效率会逐步降低。 |
为了保证数据表的磁盘页面数量能够保持在一个相对稳定值,可以定期执行该操作,如每天或每周中数据操作相对较少的时段。 相比于不执行任何VACUUM操作,其效率更高,但是插入的效率会有所降低。 |
考虑到该操作的开销,以及对其他错误的排斥,推荐的方式是,定期监控数据量变化较大的表,只有确认其磁盘页面占有量接近临界值时,才考虑执行一次该操作。即便如此,也需要注意尽量选择数据操作较少的时段来完成该操作。 在执行完该操作后,所有基于该表的操作效率都会得到极大的提升。 |
-- 该视图显示了那些膨胀的(在磁盘上实际的页数超过了根据表统计信息得到预期的页数)正规的堆存储的表。
select * from gp_toolkit.gp_bloat_diag order by bdinspname asc, bdirelpages desc, bdidiag
-- 所有对象的膨胀明细
select * from gp_toolkit.gp_bloat_expected_pages;
For a table, you can view information about the amount of unused disk space (space that is occupied by deleted or obsolete rows) in the gp_toolkit view gp_bloat_diag. If the bdidiag column for a table contains the value significant amount of bloat suspected, a significant amount of table disk space consists of unused space. Entries are added to the gp_bloat_diag view after a table has been vacuumed.
To remove unused disk space from the table, you can run the command VACUUM FULL on the table. Due to table lock requirements, VACUUM FULL might not be possible until a maintenance period.
As a temporary workaround, run ANALYZE to compute column statistics and then run VACUUM on the table to generate an accurate row count. This example runs ANALYZE and then VACUUM on the cust_info table.
參考:https://gpdb.docs.pivotal.io/43330/admin_guide/managing/maintain.html
显然 370108 > 48 其在磁盘上实际的页数远远超过了根据表统计信息得到预期的页数,这会导致严重的执行计划偏移。
1、将其vacuum full。并设定脚本定期vacuum
2、调整表的分布键。
1、PostgreSQL是通过HOT技术以及autovacuum来避免或减少垃圾的,因此可以在建表的时候使用fillfactor 减少垃圾的产生,避免表膨胀。
2、定时任务 对significant amount of bloat suspected,vacuum减少垃圾的产生。
参考:1、https://gpdb.docs.pivotal.io/43330/ref_guide/sql_commands/ALTER_TABLE.html
2、https://blog.csdn.net/MyySophia/article/details/83313115?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158492464919724845005727%2522%252C%2522scm%2522%253A%252220140713.130056874..%2522%257D&request_id=158492464919724845005727&biz_id=0&utm_source=distribute.pc_search_result.none-task