解决文件存储难题 openGauss 隆重推出段页式特性
现代社会信息数据爆炸式增长,工业界业务需求纷繁复杂。数据存储的数据量,建表数量也都不断增长。openGauss 通用的普通表,每个数据表对应一个逻辑逻辑上的大文件(最大 32T),该逻辑文件又按照固定的大小划分多个实际文件存在对应的数据库目录下面。所以,每张数据表随着数据量的增多,底层的数据存储所需文件数量会逐渐增多。同时,openGauss 对外提供 hashbucket 表、大分区表等特性,每张数据表会被拆分为若干个子表,底层所需文件数量更是成倍增长。由此,这种存储管理模式存在以下问题:
1. 对文件系统依赖大,无法进行细粒度的控制提升可维护性;
2. 大数据量下文件句柄过多,目前只能依赖虚拟句柄来解决,影响系统性能;
3. 小文件数量过多会导致全量 build、全量备份等场景下的随机 IO 问题,影响性能;
为了解决以上问题,openGauss 引入段页式存储管理机制,类似于操作系统的段页式内存管理,但是在实现机制上区别很大。
一、 段页式实现原理
在段页式存储管理下,表空间和数据文件以段(Segment)、区(Extent)以及页(Page/Block)为逻辑组织方式进行存储的分配和管理。如下图所示。具体来说,一个 database(在一个 tablespace 中)有且仅有一个段空间(segment space),实际物理存储可以是一个文件,也可以拆分成多个文件。该 database 中所有 table 都从该段空间中分配数据。所以表的个数和实际物理文件个数无关。每个 table 有一个逻辑上的 segment,该 table 所有的数据都存在该 segment 上。每个 segment 会挂载多个 extent,每个 extent 是一块连续的物理页。Extent 的大小可以根据业务需求灵活调整,避免存储空间的浪费。
图 1 段页式存储设计示意图
段页式文件可以自动扩容,不需要用户手动指定,直到磁盘空间用满或者达到 tablespace 设置的 limit 限制。段页式存储不会自动回收磁盘空间。当某些数据表被删除之后,其在段页式文件中占据的空间,会被保留,即段页式文件中会存在一些空洞,磁盘空间没有被释放。这些空洞会被后面新扩展或者创建出来的表重用。用户如果确定不需要重用这些空洞,可以手动调用系统函数,来进行磁盘空间的回收,释放磁盘空间。
内部实现上,每个 segment 对应原先页式存储的一个物理文件。比如每个分区表、每个 hashbucket 表的一个 bucket,都会有一个单独的 segment。每个 segment 下面会挂载多个 extent,每个 extent 在文件中是连续的,但 extent 之间未必连续。Segment 会动态扩展,加入新的 extent,但不能直接回收某个 extent。可以通过对整个 table 做 truncate 或者 cluster 等方式,以 segment 为粒度回收存储空间。
目前支持四种大小的 extent,分别是 64K/1M/8M/64M。对于一个 segment 来说,每一次扩展的 extent 的大小是固定的。前 16 个 extent 大小为 64K,第 17 到第 143 个 extent 大小为 1MB,依次类推。具体参数如下表所示。
表 1 segment 存储 extent 分类表
二、 段页式表使用指导
用户在用 SQL 语句 create table 建表时可以通过指定参数 segment=on,使得行存表可以使用段页式的方式存储数据。如果指定 hashbucket=on,则默认强制使用 segment=on。目前段页式存储不支持列存表。段页式表空间是自动创建的,不需要用户有额外的命令。
1. 指定参数 segment=on,创建段页式普通表
2. 指定参数 hashbucket=on,创建段页式 hashbucket 表
为了让用户更好使用段页式功能,openGauss 提供了两个 built in 的系统函数,显示 extent 的使用情况。用户可以使用这两个视图,决定是否回收和回收哪一部分的数据。
pg_stat_segment_space_info(Oid tablespace, Oid database); 参数是 tablespace 和 database 的 Oid,输出位于该表空间下所有 ExtentGroup 的使用信息。
表 2 pg_stat_segment_space_info 视图字段信息
pg_stat_segment_extent_usage(Oid tablespace, Oid databse, uint32 extent_type); 每次返回一个 ExtentGroup 中,每个被分配出去的 extent 的使用情况。extent_type 表示 ExtentGroup 的类型,合理取值为[1,5]的 int 值。在此范围外的会报 error。
表 3 pg_stat_segment_extent_usage 视图字段信息
gs_spc_shrink(Oid tablespace, Oid database, uint32 extent_type);每次清理一个 ExtentGroup。Shrink 的目标大小是自动计算出来的,为 active 的数据量 + 128MB,然后向上取整和 128MB 对齐。
三、 总结
openGauss 为了解决 hashbucket 表、大分区表数量较多时,底层文件句柄过多的问题,提供了段页式解决方案。段页式对外将表对应逻辑上的一个段(segment),底层不同的 segment 存储在一个物理文件上,大大减少了底层物理文件的句柄。即使在大数据量下,也避免了普通表那种文件句柄过多的场景,提升了系统可维护性。同时,对于全量 build、全量备份等场景,减少小文件数量过多引起的随机 IO,可以提升系统 IO 性能。同时可以看到当前段页式表相关的参数都是固定的,未来 openGauss 可以探索利用 AI 技术,对段页式存储机制进行参数自动调参,从而可以为用户提供更智能,性能更优的段页式存储策略。
评论