GaussDB for DWS 高可用之数据复制
1 前言
数据库中高可用普遍使用的是Log Shipping方式,即将WAL日志采用streaming方式传输给备实例,备实例通过重放该日志得到主实例的数据。
对于行存储方式使用Log Shipping方式效率较高,但是对于列式存储,通常都是批量导入,如果同样将数据记录到日志中,会使数据写两遍IO,难以发挥列存储优势,因此通常采用数据复制方式(即数据从主实例的内存中直接通过网络传输给备实例,然后备实例写盘)更为合理。在行存的批量导入中,使用数据复制不仅可以提升传输效率(直接从内存中读取数据,无需通过磁盘读取日志),还可提升备实例恢复效率(直接从内存中恢复,无需将日志先写盘,再读取恢复)。
GaussDB for DWS的复制方式采用日志和数据混合方式,对于行存单条insert/update/delete,然后使用日志复制方式,对于行存批量导入(copy/insert into select from)默认采用数据复制方式;对于列存默认采用数据复制方式。
下面主要介绍下GaussDB for DWS中数据复制的主要设计。
2 数据复制总体设计
在GaussDB for DWS中,数据复制默认打开,分别在行存批量导入和列存导入时使用。整体设计如下图所示:
在数据导入过程中,每个业务线程Backend将收到的数据组成一个block(列存为一个CU)放到Datasend线程的数据队列(DataSenderQueue)里,Datasend线程将数据队列数据发送给Datareceive线程,Datareceive线程将接收的数据写入到数据队列(DataWriterQueue)中,Datarcvwrite线程从数据队列(DataWriterQueue)中取出数据按block分别写入到磁盘,实现主备复制高可用。
2.1 Dataqueue设计
Dataqueue是一块共享内存,用于实现DataSenderQueue和DataWriterQueue,其算法核心是循环使用共享内存,如下图所示:
首先tail1,head2,tail2初始化为0,每个变量为两个uint32位,第一个uint32 queueid表示数据队列使用了第几次,第二个uint32 queue offset表示当前数据队列的offset。
数据导入后,首先tail2向后递增,各个导入通过锁控制实现并发,Datasend发送数据后将head2向后移动,当tail2到达末尾时,新导入数据将移动tail1指针,当tail1与head2接近时,表明没有缓存空间了,因此需要等待Datasend发送数据后将head2向后移动腾出空间。整体算法是一个循环利用实例制,数据的offset是一个单调递增的过程,类似WAL日志的LSN。
2.2.2 同步提交设计
在GaussDB for DWS中,数据冗余使用的是同步提交,即数据写完两份后事务提交。WAL日志中通过比较LSN非常简单的实现了同步提交。类似WAL日志,数据复制由于Dataqueue中offset也是有序递增的,因此也很容易实现同步提交。其过程是Datarcvwrite将数据单元写入磁盘后,刷新写盘offset,Datareceive线程通过心跳信息将写盘offset发送给Datasend,各业务线程在事务提交前check本次导入最后一段数据备实例端已经写盘后即可提交。
2.2.3 BCM与Catchup设计
由于GaussDB for DWS设计之初考虑到RAID5数据冗余存储,因此上层只存储两副本,由于分布式环境下各个数据节点采用异步提交时可能会导致全局数据不一致,因此各个数据节点必须使用同步提交。那么系统中如果坏了一个任何一个数据节点的主实例或者备实例系统便不可进行写事务操作。然而在大型分布式系统中这是往往不能接受的,必须要支持单节点故障。
GaussDB for DWS使用了缓存接收数据(从备)来实现同步提交下的单节点故障。那么就是备实例挂了后,数据将发往从备,等备实例修复后,备实例可以从主实例同步数据,如何区分哪些数据未同步便显得尤为重要。
这里使用BCM(bit change map)文件来实现哪些block需要在追赶时发送给备实例,哪些不用发送。具体设计如下:
当数据导入时,每个block对应BCM页面中两个bit(第一个bit用于标记是否同步,第二个保留),放入发送队列后将BCM页面中两个bit第一个标记为未同步,当数据已经写入到备实例端后,将对应block的BCM页面bit标记为已经同步。
当备实例挂掉时,数据发送给从备后,BCM页面对应bit将不再清理未同步标记,任然为未同步。当备实例起来后,连接主实例,主实例启动catchup线程先获取从备上增量数据列表,然后扫描本地所有未同步数据发送给备实例,实现主备同步。
2.2.4 数据复制与日志复制并发控制
数据复制和日志复制是并发运行的,当数据复制正在写入数据时,日志复制无法恢复删除表与truncate表等操作,两者通过锁控制。同样当数据复制时要写入的database与表空间还未恢复时,同样需要等待日志复制恢复完成,这里通过循环检测等待实现。
3 数据复制相比日志复制
当IO负载较高时,批量导入场景数据复制的性能优于日志复制30%左右,因此在大数据量入库时,使用数据复制性能更佳。然而对于行存来说触发数据复制时每次会获取新页面,因此对于insert into t1 values v1,v2,v3这种值较少的场景下会导致大量的空页面,不适用于数据复制,可以手动关闭数据复制开关。
- 点赞
- 收藏
- 关注作者
评论(0)