摘要:Bucket存储是数据共享中重要的一环,当前阶段,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数据: