在某些情况下,因为底层存储架构的原因,Greenplum的Heap表很容易出现膨胀(Bloat)。Bloat会影响表的扫描性能,从而影响查询性能。
1. 什么是表膨胀?
表膨胀是指在一张表的数据文件中积累的自由空间(free space)被旧数据行使用。这些空间已经被之前删除或者不再访问的数据使用。不能做表的维护以重用这些空间,导致表数据文件越来越大,所以表扫描需要更长的时间。
2. 导致表膨胀的原因是什么?
Greenplum数据库的存储实现(MVCC-多版本并发控制)来自于Postgres。根据MVCC的原理,没有办法直接更新数据(更新操作(update)是通过先删除(delete)再插入(insert)实现的),被更新之前的行数据仍然在数据文件中,直到通过使用VACUUM命令使空间被标记为“free”。
一旦VACUUM将被删除的行数据标记为“free space”,这些空间就能够被将来的插入和更新操作使用。在更新操作后,VACCUM操作之前的这段时间,这些空间是没有标记为“free”,因此无法被重新使用,实际上这些空间为“dead space”。
3. 如何去识别表膨胀?
对于表膨胀,我们可以使用gp_toolkit的模式下面的几张表来查看。
A. gp_toolkit.gp_bloat_diag
视图,显示膨胀表的诊断信息。
描述一下字段信息:
· bdirelid:表的OID (pg_class.oid)
· bdinspname:表所属Schema
· bdirelname:表名
· bdirelpages:当前表数据文件中的pages数量
· bdiexppages:根据当前统计信息,期望的pages数量
· bdidiag:bloat诊断结果 (比率1到3表示:no bloat;比率从4到10表示:moderate bloat;比率从ratio大于10表示:significant bloat)
B. gp_toolkit.gp_bloat_expected_pages
视图,显示数据库中所有表的page数据(currentpages/expected pages)。
描述一下字段信息:
· btdrelid:表的OID (pg_class.oid)
· btdrelpages:表数据文件中,当前pages数量
· btdexppages:根据当前统计信息,期望的pages数量
zy_db=# select *from gp_toolkit.gp_bloat_expected_pages limit 5;
btdrelid |btdrelpages | btdexppages
----------+-------------+-------------
10784 | 1 | 40
10789 | 1 | 40
10794 | 1 | 40
10799 | 1 | 40
5004 | 1 | 40
(5 rows)
注意事项:
这些视图依赖于表的统计信息进行计算bloat大小,因此表的统计信息要更新到最新的时间。
Greenplum数据库的系统表是Heap表。因此很容易出现bloat。系统表对数据库的性能是很重要的,推荐定期对系统表进行vacumm。
如果数据库中对象改变(create/drop/alter table)很少,建议每周vacuum,如果数据库中对象改变比较频繁,建议每天一次。
4. 如何避免和消除膨胀表
使用VACUUM命令去标记表的数据文件中被删除记录的空间为“free space”,用来重新使用这部分空间,因此我们需要消除表的膨胀。对于一张表来说,大量的update/delete/insert操作肯定会产生已删除行/空闲空间,这些空间将被新的数据重用。
通常情况下使用VACUUM可以保证被删除的数据行作为“free space”被重利用。但是这一些情况下,为了消除额外被占用的空间(如果从来没有被使用重新),需要使用VACUUM FULL命令,这个命令将会影响表数据,通过把表数据移动到数据文件的前面并且truncate数据文件末尾未被使用的空间,即涉及到空间合并操作。VACUUM FULL将会逐行操作,因此对大表来说,速度非常慢,并且在表上添加排它锁(exclusive lock)。
VACUUM FULL建议在Greenplum数据库维护阶段执行,并且确保充分考虑运行时间和效果。
VACUUM FULL一旦运行不应该被用户终止掉。
相对VACUUM FULL来说,对用户数据表更好的选择是重分布表(不可以对系统表进行重分布),有效地在重构建表时消除膨胀。具体操作如下:
A. 查看表的分布键
B. ALTER TABLESET with (REORGANIZE=false) DISTRIBUTED randomly;
这一步仅仅标记表,并不会移动数据,命令立刻就执行完了。
C. ALTER TABLE SET with (REORGANIZE=true)DISTRIBUTED BY (分布键字段);
重新写数据文件,因为分布键的原因,其实并没有在数据文件的层面发生变化,也不会通过网络发送数据,只会在本地发生数据重写。简单来说,就是表数据是根据分布键分布到Greenplum集群的每个segment上,这步操作不会移动一个segment数据到另一个segment上,只会在自身segment上重写数据文件。
注:
VACUUM FULL和替代VACCUM FULL的表重分布仅在特殊的情况下才使用。
定期在用户数据表(insert/update/delete)以及系统表上执行VACUUM。