此前,带你读源码第七篇:《一文读懂数据库索引实现原理》尝试从代码导读的角度,简要介绍了 OceanBase 的索引构建流程,带领大家读懂索引构建的相关代码。
本期“源码解读”由 OceanBase 开发工程师刻晴为大家带来“事务日志的提交和回放”。OceanBase 的日志( clog )类似于传统数据库的 REDO 日志,这个模块负责在事务提交时持久化事务数据,并实现了基于 Multi_Paxos 的分布式一致性协议。
日志模块的设计理念
与传统关系型数据库的日志模块相比,OceanBase 的日志服务主要有如下几点挑战:
- 通过 Multi_Paxos 替换传统的主备同步机制,进而实现系统的高可用和数据的高可靠。
- 需要支持全球部署,多个副本之间的网络时延达到几十甚至数百 ms。
- 作为分布式数据库,OceanBase 以 Partition 作为数据同步的基本单位。单机要求支持十万量级的 Partition,在此基础上,需要支持日志数据的高效写入和读取。
- 同时,任何功能本身均需要考虑操作的批量化,以加速执行速度。日志服务维护副本的成员列表、Leader 等状态信息,需要为分布式系统的丰富功能提供高效的支持。
日志的一生
OceanBase 的日志模块实现了一套标准的 Multi_Paxos,保证在未发生多数派永久性故障的前提下,所有提交成功的数据均可恢复。
同时实现了乱序日志提交,保证事务之间无依赖。在下面关于 OceanBase 对 Multi_Paxos 的工程实现介绍前,我们默认读者已经了解 Multi_Paxos 的核心思想,如果你还想了解更多,欢迎在社区问答区对 Multi_Paxos 进行了解。
以一个分区的一条事务日志为例,正常的流程大致如下:
1、事务层调用 log_service->submit_log() 接口提交日志(其中携带 on_succ_cb 回调指针)。
2、clog 首先为它分配 log_id和submit_timestamp,然后提交到滑动窗口中,生成一个新的 log_task,本地提交写盘,通过 RPC 将日志同步给 follower。
3、本地写盘完成时调用 log_service->flush_cb(),更新 log_task 状态,标记本地持久化成功,follower 写盘成功后给 leader 回 ack。
4、leader 收到 ack 更新 log_task 的 ack_list。
5、leader 在 4、5 两步中会统计多数派,一旦达成 majority,按顺序调用 log_task->on_success 回调事务,同时发送 confirmed_info 消息给 follower ,并将此日志从滑动窗口中滑出。
6、follower 收到 confirmed_info 消息后尝试将此日志从滑动窗口中滑出,在滑出动作中将日志提交回放。
在 follower 上,会对已提交的日志实时进行回放。回放流程大致如下:
follower 在滑出日志时会推大每个分区内记录回放位点的 submit_log_task 的对应值,这个任务会被异步的提交到一个全局线程池中等待消费。
当全局线程池消费某分区的 submit_log_task 时,会将其中记录的所有待回放日志以此读盘并分配到所在分区的对应的 4 个回放任务队列中,分配的方式主要是按照 trans_id 进行哈希,确保同一事务的日志被分配到一个队列中,被分配到新任务的队列将队列 task_queue 作为一个任务再次异步提交到 1 中提到的全局线程池中等待消费。
当全局线程池消费 task_queue 时,会依次遍历队列中所有子任务,并根据日志类型执行相应的应用逻辑。至此,一个日志就真正同步到了 follower 中,在 follower 上也可读了。
学完本篇源码解读,希望你对“事务日志的提交与回放”有了相应了解,下期我们将为大家带来 OceanBase 存储层代码解读的相关内容,敬请期待!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。