教你两招,解决数据膨胀
摘要:为了解决数据膨胀,GaussDB(DWS)通过 vacuum 和 FSM 来清理和重用物理空间。本文简单介绍 FSM 的设计和原理,并通过一个例子对 FSM 功能进行简单的测试和验证。
本文分享自华为云社区《数据膨胀了?又没有空间了?一招教你解决》,原文作者:QWERT886。
数据膨胀,指的是物理数据文件的大小明显高于实际存储的数据量。甚至某些特殊场景下,一个表中只有一条简单的数据,但是表对应的物理文件可能已经达到 M 级甚至 G 级。
为了解决数据膨胀,GaussDB(DWS)通过 vacuum 和 FSM 来清理和重用物理空间。本文简单介绍 FSM 的设计和原理,并通过一个例子对 FSM 功能进行简单的测试和验证。
数据膨胀的原因
想弄清楚数据膨胀的原因,首先要了解 GaussDB(DWS)行存表数据基于 MVCC 的存储机制:
1. INSERT 很简单,就是将元组插入到页面的空闲空间中;
2. DELETE 则是将元组标记为旧版本,但是即使这个旧版本对所有事务都不可见了,这个元组占用的空间也不会归还给文件系统;
3. UPDATE 相当于 DELETE+INSERT,等于是占用了两条元组的位置,类似 DELETE,旧版本的元组依然占用着物理空间。
很明显,在一通增删改操作之后,页面上的旧版本元组势必是占有一定比重的。这就导致了物理文件大小明显高于实际的数据量。
设计方案
为了解决无效元组占用空间的问题,GaussDB(DWS)提供了 vacuum 功能,在旧版本元组过期(对所有事务都不可见)后,vacuum 可以将元组物理删除,这样页面上被清理出来的空闲空间就可以被再次使用了。
但是每个页面的空闲空间又不是固定大小的,所以如果要利用这些空间空间,就需要遍历一遍数据页面来找到它们,但是这样会造成比较大的开销。因此就设计了用来记录每个页面剩余空间的空闲空间映射表 FSM(Free Space Mapping),以便高效的将空闲空间管理起来,方便查找和重新使用。
实现过程
FSM 是以 _fsm 为后缀的文件对外展现的,每个行存表都有一个 fsm 文件。在表创建时,fsm 文件并不会一起创建出来,而是在第一次 vacuum 时才会被创建。
因为不同页面上的元组长度各不相同,为了快速高效的管理空闲空间,没必要非常精确的管理每一个字节。将一个 8K 的数据页面(datablock)分成 256 份,从页面头到页面尾顺序计算,排除页面头等固定支出,最多可以有 255 份空闲空间,这样 FSM 用 1 个 Byte 就可以标识出一个数据页面的空闲空间的大小。
在空闲空间查询时,我们只需要找到能满足需求的页面即可,所以 FSM 将每个页的空余空间信息通过一个大根堆结构进行维护。这样只需要从堆的根获取当前最大的剩余空间就可以知道有没有能符合要求的页面。堆中的每个叶子节点都对应一个数据页,叶子节点上记录的是数据页的可用单元的个数。
然后 FSM 机制通过在不同的 FSM 页间维护了一个类似 FSM 本身的树形结构,来管理所有的 FSM block:一个 3 层的多叉树结构。
FSM 页面也是大小为 8K 的块(FSM block),所以每个 FSM block 最多可以描述 4096 个数据页面(粗略计算,肯定是达不到的,因为有页头等信息)。按照 3 层计算:4096(0 层)4096(1 层)(8k*4096)(2 层) = 2PB。可以管理 2PB 的数据,这对描述一张行存表,完全够用了。
整个 FSM 机制如下图:
1. level root 和 level medium 都是用来查找 level 2 中的 FSM 页面的
2. level bottom 是用来查找符合要求的 heap 页面的
FSM 信息的可视化读取
FSM 查找和维护的逻辑并不复杂,但是整个过程对外是不可见的。因此 GaussDB(DWS)提供了 pagehack 工具来读取 FSM 文件,帮助查看当前数据页的空闲空间情况。
下面结合 pagehack 工具解析 FSM 文件进一步理解 FSM 机制:
初始化数据
1. 首先新建行存表并插入大量数据。分布列数据固定,为了让数据都落入一个 dn,方便后面分析。
2. 删掉 2 条位于第一个 heap page 的数据。因为是新建的表,所以数据会从前往后顺序的落到数据页面里,c2 等于 1 和 2 的两条数据一定在第一个页面上。
生成 FSM 文件并落盘
用 pagehack 解析 FSM 文件
1. pagehack 解析 FSM 文件并输出到文件中
2. 打开文件,从第一个 fsmblock 开始看。第一个 fsm block 属于 levelroot,看到一共 111 个 fsm block,下层的最大空闲空间是 31,且在数组 0 的位置上
3. 接着看到第二个 fsmblock,属于 level medium,记录的最大空闲空间为 31,数组 0 位置代表下层的 fsm block 0 有 2 个 slot,数组 108 位置代表 fsmblock 108 有 31 个 slot,其他都是 0。0~108 中间的 0 表示没有这些页面都没有空闲空间了,108 之后的表示页面还没有扩展出来
4. 再往后面是第三个 fsmblock,这个及以后的 block 都属于 levelbottom,这层的 FSM 页面都是直接对应数据页面的。可以看到最大剩余空间为 2,数组 0 位置代表 heap page 0 有 2 个位置,正好是刚才删除的两条数据。
5. 第 4 个及后面的一直到 110 的 block 的信息如下,可以看到整个 heap page 都没有剩余空间了,这是因为这些页面一直在插入,没有删除数据。
6. 第 111 个 block 显示,最大空闲空间为 31。FSM 总块 111 个,块号 110,减去前面的两个非叶子层的 block,为 108,正好对应前面第二个 block 中的第 108 个 slot(存 31)。
从 12553 这行开始算,到 12628 的第 26 个位置为 31,(12628-12553)40+26 = 3026。用 page range 算:439452+3026 = 442478。按 8k 页面算: 4424788192 = 3,624,779,776;
看一下实际文件大小:1073741824*3+403554304= 3,624,779,776,与刚才算的结果相同。
思考总结
总结刚才的测试:
1. 我们一开始向空表顺序插入了大量的数据,页面也是顺序的扩展
2. 在 442478 页面的时候,最后的数据插入完毕,并且还留有 31 个空间可用
3. 当我们从第一个页面删除了两条数据后,第一个页面空余出了 2 个空间
4. fsm 树的样子类似:
5. 在 level bottom 这层的 fsmblock 中,按顺序存放的就是 heap block 的空闲空间值。
6. 1 亿条数据用了 108 个 slot,而一个 fsm block 有 4000+个叶子,所以肯定是用不完的。
对于日常运维的建议是:
打开 autovacuum,让 GaussDB(DWS)自动的帮你做 vacuum,这样新的数据就可以通过 FSM 机制复用空闲页面,以减少数据膨胀带来的磁盘空间浪费。
想了解 GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的 PB 级数仓黑科技~
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/92e7e76dd463b453fdf21f9eb】。文章转载请联系作者。
评论