表膨胀问题分析
1、什么是表膨胀?
表膨胀是指在一张表的数据文件中积累的自由空间(free space)被旧数据行使用。这些空间已经被之前删除或者不再访问的数据使用。不能做表的维护以重用这些空间,导致表数据文件越来越大,所以表扫描需要更长的时间。
2、导致表膨胀的原因是什么?
Greenplum数据库的存储实现(MVCC-多版本并发控制)来自于Postgres。根据MVCC的原理,没有办法直接更新数据(更新操作(update)是通过先删除(delete)再插入(insert)实现的),被更新之前的行数据仍然在数据文件中,直到通过使用VACUUM命令使空间被标记为“free”。
一旦VACUUM将被删除的行数据标记为“free space”,这些空间就能够被将来的插入和更新操作使用。在更新操作后,VACCUM操作之前的这段时间,这些空间是没有标记为“free”,因此无法被重新使用,实际上这些空间为“dead space”。
3、如何去识别表膨胀?
对于表膨胀,我们可以使用gp_toolkit的模式下面的几张表来查看。
3.1 gp_toolkit.gp_bloat_diag
-- 该视图显示了那些膨胀的(在磁盘上实际的页数超过了根据表统计信息得到预期的页数)正规的堆存储的表。
select * from gp_toolkit.gp_bloat_diag order by bdinspname asc, bdirelpages desc, bdidiag
视图,显示膨胀表的诊断信息。
描述一下字段信息:
· 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)
3.2 gp_toolkit.gp_bloat_expected_pages
-- 所有对象的膨胀明细
select * from gp_toolkit.gp_bloat_expected_pages;
视图,显示数据库中所有表的page数据(currentpages/expected pages)。
描述一下字段信息:
· btdrelid:表的OID (pg_class.oid)
· btdrelpages:表数据文件中,当前pages数量
· btdexppages:根据当前统计信息,期望的pages数量
vacuum命令
vacuum命令以及vacuum full对比
**vacuum:**不会锁表,只是单纯的回收空间以被重用,被回收的空间一般情况不会被返还给操作系统,仅仅被保留在同一个表中以备重用。
**vacuum full:**会锁表,会将表的整个内容重写到一个新的磁盘文件中,并且不包含额外的空间,这使得没有被使用的空间被还给操作系统。
无vacuum | VACUUM | VACUUM FULL | |
---|---|---|---|
删除大量数据之后 | 只是将删除数据的状态置为已删除,该空间不能记录被重新使用。 | 如果删除的记录位于表的末端,其所占用的空间将会被物理释放并归还操作系统。如果不是末端数据,该命令会将指定表或索引中被删除数据所占用空间重新置为可用状态, 那么在今后有新数据插入时,将优先使用该空间,直到所有被重用的空间用完时,再考虑使用新增的磁盘页面。 | 不论被删除的数据是否处于数据表的末端,这些数据所占用的空间都将被物理的释放并归还于操作系统。之后再有新数据插入时,将分配新的磁盘页面以供使用。 |
执行效率 | 由于只是状态置为操作,因此效率较高。 | 在当前版本的PostgreSQL(v9.1)中,该命令会为指定的表或索引重新生成一个数据文件,并将原有文件中可用的数据导入到新文件中,之后再删除原来的数据文件。因此在导入过程中,要求当前磁盘有更多的空间可用于此操作。由此可见,该命令的执行效率相对较低。 | |
被删除的数据所占用的物理空间是否被重新规划给操作系统。 | 不会 | 不会 | 会 |
在执行VACUUM命令时,是否可以并发执行针对该表的其他操作。 | 由于该操作是共享锁,因此可以与其他操作并行进行。 | 由于该操作需要在指定的表上应用排它锁,因此在执行该操作期间,任何基于该表的操作都将被挂起,知道该操作完成。 | |
推荐使用方式,执行后其它操作的效率 | 在进行数据清空时,可以使用truncate操作,因为该操作将会物理的清空数据表,并将其所占用的空间直接归还于操作系统。对于查询而言,由于存在大量的磁盘页面碎片,因此效率会逐步降低。 | 为了保证数据表的磁盘页面数量能够保持在一个相对稳定值,可以定期执行该操作,如每天或每周中数据操作相对较少的时段。 相比于不执行任何VACUUM操作,其效率更高,但是插入的效率会有所降低。 | 考虑到该操作的开销,以及对其他错误的排斥,推荐的方式是,定期监控数据量变化较大的表,只有确认其磁盘页面占有量接近临界值时,才考虑执行一次该操作。即便如此,也需要注意尽量选择数据操作较少的时段来完成该操作。 在执行完该操作后,所有基于该表的操作效率都会得到极大的提升。 |
使用VACUUM FULL
VACUUM FULL建议在Greenplum数据库维护阶段执行,并且确保充分考虑运行时间和效果。
VACUUM FULL一旦运行不应该被用户终止掉。
相对VACUUM FULL来说,对用户数据表更好的选择是重分布表(不可以对系统表进行重分布),有效地在重构建表时消除膨胀。具体操作如下:
1、 查看表的分布键
select n.nspname AS “模式名”,c.relname AS “表名”,case when p.policytype=‘p’ then ‘分区表’ when p.policytype=‘r’ then ‘复制分布’ end “分布策略”, pg_get_table_distributedby(c.oid) “分布键” ,c.reloptions “压缩类型”
from pg_class c
left join gp_distribution_policy p on c.oid=p.localoid
left join pg_namespace n on c.relnamespace=n.oid
where c.relname in (‘
t
a
b
l
e
n
a
m
e
′
,
′
tablename','
tablename′,′tablename’)
and n.nspname=‘$schemaname’
2、标记表
ALTER TABLE SET with (REORGANIZE=false) DISTRIBUTED randomly;
这一步仅仅标记表,并不会移动数据,命令立刻就执行完了。
3、重新设置分部
ALTER TABLE SET with (REORGANIZE=true)DISTRIBUTED BY (fundaccoundid);
重新写数据文件,因为分布键的原因,其实并没有在数据文件的层面发生变化,也不会通过网络发送数据,只会在本地发生数据重写。简单来说,就是表数据是根据分布键分布到Greenplum集群的每个segment上,这步操作不会移动一个segment数据到另一个segment上,只会在自身segment上重写数据文件。
注:
VACUUM FULL和替代VACCUM FULL的表重分布仅在特殊的情况下才使用。
定期在用户数据表(insert/update/delete)以及系统表上执行VACUUM。