bucket 表:数仓存算分离中 CU 与 DN 解绑的关键

本文分享自华为云社区《存算分离之bucket表——【玩转PB级数仓GaussDB(DWS)】》,作者: yd_278301229 。
在云原生环境,用户可以自由配置 cup 型号、内存、磁盘、带宽等资源,需要在计算和 IO 之间做平衡;如果计算和存储耦合,扩缩容时数据要在节点之间移动,同时还要对外提供计算,性能会大受影响。如果存算分离,计算出和存储层可以独立增加节点互不干扰,这其中一个关键点是做到数据共享。Bucket 存储是数据共享中重要的一环,当前阶段,bucket 存储可以将列存中的 CU 数据和 DN 节点解绑。
一、bucket 表在存算分离中的作用
通过存算分离,把 DWS 完全的 shared nothing 架构改造成计算层 shared nothing + 存储层 shared storage。使用 OBS 替换 EVS,OBS 对 append only 存储友好,与列存 CU 存储天然适配;由于存算分离数据共享,对写的并发性能不高,在 OLAP 场景下读多写少更有优势,这一点也是和列存相匹配的,目前主要实现的是列存的存算分。在当前。bucket 表在存储层共享中,为了将 CU 数据和 DN 节点解绑,主要做了两件关键的事,CUID 和 FILEID 全局统一管理。我们来看看为什么这两件事能把 CU 和 DN 节点解绑以及带来的好处。为了解释这个问题,先看看目前 shared nothing 架构中,建库和存储数据的过程。
二,当建立一张列存表并存储数据时,我们在做什么
建一张列存表时,主要要做以下两步:
1,系统表中建立表的数据。
2,为列存建立 CUDesc 表、Delta 表等辅助表
当存储数据时,主要做以下几步:
1,根据数据分布方式,决定数据存储到哪个 DN。
2,把列存存储时需要的辅助信息填入 CUDesc 表、Delta 表等辅助表。
3,把存储用户数据的 CU 存储本地 DN。
在上面的过程中,由于 DN 之间互不干扰,那就需要各自管理自己的存储的表的信息。
CUDesc 表的一大功能是 CU 数据的“指路牌”,就像指针一样,指出 CU 数据存储的位置。靠的是 CUID 对应的 CUPoint(偏移量),加上存储在 DN 的文件位置就能标注出具体的 CU 数据,而文件名就是系统表中的 relfilenode。

由于在 MPPDB 的存算一体中,数据都存储在 DN 节点,DN 节点之间互不干扰,CUID 和 relfilenode 各个 DN 节点自己管理,只要自己不出问题就行了,也就是“各人自扫门前雪莫管他人瓦上霜”,例如下图,显示一张列存表在集群中的存储状态。

CN 把要存储的数据根据分布算法(例如对 DN 数量做除法取余数)把 1,3,5 存到 DN1,把 2,4,6 存到 DN2。DN1 此时生成存储 CU 文件的 relfilenode 是 12345,每插入一次 CUID,就把该表的 CUDesc 表 CUID 自增,DN1 只要把自己的数据管理好,与 DN2 无关。DN2 同理。
三,数据共享和扩缩容时,遇到的困难
1,数据共享时遇到的困难
如上所述,当用户想查询数据 1,2,4,6 时,该怎么办。因为 DN1 和 DN2 都不可能单独完成任务,就需要共享数据了。问题就来了,DN 间肯定是想以最小代价来完成数据共享,系统表最小,CUDesc 表也很小(就像指针一样,同等规模下,只有 CU 数据的 1/3000 左右),CU 数据最大。假如最后决定以 DN2 来汇聚所有结果,就算 DN1 把系统表和 CUDesc 表中的数据传给 DN2,DN2 也看不懂,因为在 DN2 上,relfilenode 为 12345 可能是另外一张表,cudesc 表为中 CUID 为 1001 的 CUPoint 也不知道是指向哪儿了(data1,data2),没办法,只能是 DN1 自己计算,最后把 data1 的 CU 数据通过 stream 算子发送给 DN2。DN1 迫不得已选了最难的那条路,CU 的数据量太大,占用了网络带宽,还需要 DN1 来参与计算,并发上不去。

2,扩缩容时遇到的困难
当用户发现 DN1,DN2 节点不够用时,想要一个 DN3,该怎么办。根据分布假设的算法(对 DN 数量做除法取余数),data3 和 data6 应该要搬去 DN3 才对。系统表还比较好搬,无非是在 DN3 上新建一份数据,CUDesc 表也好搬,因为数据量小,再把 CUID 和 CUPoint 按照 DN3 的逻辑写上数据就好了,CU 数据也要搬,但是因为 CU 数据量大,会占用过多的计算资源和带宽,同时还要对外提供计算。真是打了几份工,就赚一份工资。

总结起来,困难主要是
1)系统表元数据(计算元数据)
每个节点有自己的系统表元数据,新增 dn 必须创建“自己方言”的系统表,而实际上这些系统表内容是“相同的”,但是 dn 之间互相不理解;
2)CUDesc 元数据(存储元数据)
每个节点的 CUID 自己分配,同一个 CUID 在不同节点指向不同的数据,CU 无法在 dn 之间迁移,因为迁移后会混乱,必须通过数据重分布生成 dn “自己方言的 CUID”
CU 的可见性信息(clog/csnlog)各自独立管理,dn1 读取 dn2 的 cudesc 行记录之后,不知道记录是否可见
3)CU 用户数据
filepath(relfilenode) dn 节点各自独立管理,dn1 不知道到哪里读取 dn2 的数据
四、揭秘 CU 与 DN 解绑的关键——bucket 表
1,存储映射
bucket 表的存储方式如图,CU 的管理粒度不再是 DN,而是 bucket,bucket 是抽象出来的一个概念,DN 存储的,是系统元数据和 bucket 对应的 CUDesc 元数据,由于在 bucket 作为存储粒度下,CUID 和 FILEID 是全局统一管理的,DN 只需要懂全局的规则,并且拿到别的 bucket 对应的 CUDesc 元数据,那就可以很方便的去 OBS 上拿到 CU 数据了。通过这种方式,把本该存储在 DN 上的 CU 数据,映射到 OBS 上,可以保证 DN 间共享数据时相互独立,换句话说,每个 DN 都读的懂其他 DN 的 CUDesc 数据,不再需要把 CU 数据喂到嘴边了。

2,全局 CUID 和 FILEID 表生成
CUID 不再是各节点自己管理生成,而是全局唯一的。其原因是 CUID 与 bucket 号绑定,特定的 bucket 号只能生成特定的 globalCUID,与此同时,relfilenode 不再作为存储的文件名,而是作为存储路径。CU 存放的文件名为 relfilenode/C1_fileId.0,fileId 的计算只与 bukcet 号和 seqno 有关。
这样 V3 表就建立起了一套映射关系(以 V2 表示存算一体表,V3 表示为 bucket 表):
step 1,数据插入哪一个 bucket 由分布方式来确定,例如是 RR 分布,那么就是轮巡插入 bucket。
step 2,CUID 是多少,由 bucket 粒度级别的 CUID 来确定,比如+1 自增作为下一个 CUID。
step 3,在 bucket 上存储 bucket 粒度级别的 fileId。
step 4,生成全局唯一 fileId,由 bucketid 和 bucket 粒度级别的 fileId 来生成,对应生成的 CU 插入该 fileId 文件名的文件。
setp 5,生成全局唯一 globalCUID。由 bucketid 和 bucket 粒度级别的 CUID 计算得到全局唯一的 globalCUID。
CUDesc 表中,也为 bucket 表新建了一个属性 fileId,用来让 DN 查找到 OBS 上的 CU 数据所在的文件。

3,共享数据和扩缩容的便利
如上所述,DN 可以通过全局 CUID 和 FILEID 来找到 CU 数据,在数据共享时,不再需要其他 DN 参与大量的计算和搬迁 CU,扩缩容时,也不需要搬迁 CU 了,只需要正确生成系统表中的信息和搬迁 CUDesc 即可。
最后,来看一看 bucket 表在 OBS 上存储的 CU 数据:

【一起来玩转PB级数仓GaussDB(DWS),分享你的技术经验与体验心得,赢开发者大礼包!】第 19 期有奖征文火热进行中!
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/5ad5cd35ec5d48458a5aaec19】。文章转载请联系作者。
评论