2026-05-24 20:14:44
OceanBase 源码解读(十二):事务日志的提交和回放
OceanBase 的 clog(commit log)日志是其事务处理的核心组件之一,类似于传统数据库的 REDO 日志。clog 负责在事务提交时持久化事务数据,并实现了基于 Multi-Paxos 的分布式一致性协议。以下是对 OceanBase 事务日志提交和回放流程的详细解读。
一、日志模块的设计理念
OceanBase 的日志服务相较于传统关系型数据库的日志模块,面临了更多的挑战。这些挑战包括但不限于:
二、日志的一生
OceanBase 的日志模块实现了一套标准的 Multi-Paxos 协议,保证在未发生多数派永久性故障的前提下,所有提交成功的数据均可恢复。同时,OceanBase 实现了乱序日志提交,保证事务之间无依赖。
以一个分区的一条事务日志为例,正常的流程大致如下:
事务层提交日志:
事务层调用 log_service->submit_log() 接口提交日志,该接口携带 on_succ_cb 回调指针。
clog 分配与提交:
clog 为日志分配 log_id 和 submit_timestamp。
日志被提交到滑动窗口中,生成一个新的 log_task。
本地提交写盘,并通过 RPC 将日志同步给 follower。
本地写盘与回调:
本地写盘完成时调用 log_service->flush_cb()。
更新 log_task 状态,标记本地持久化成功。
follower 写盘成功后给 leader 回 ack。
leader 更新 ack 列表:
leader 收到 ack 后更新 log_task 的 ack_list。
达成多数派与回调:
leader 统计多数派,一旦达成 majority,按顺序调用 log_task->on_success() 回调事务。
同时发送 confirmed_info 消息给 follower,并将此日志从滑动窗口中滑出。
follower 回放日志:
follower 收到 confirmed_info 消息后,尝试将此日志从滑动窗口中滑出。
在滑出动作中,将日志提交回放。
三、日志的回放流程
在 follower 上,会对已提交的日志实时进行回放。回放流程大致如下:
推大回放位点:
follower 在滑出日志时,会推大每个分区内记录回放位点的 submit_log_task 的对应值。
这个任务会被异步提交到一个全局线程池中等待消费。
全局线程池消费任务:
当全局线程池消费某分区的 submit_log_task 时,会将其中记录的所有待回放日志读盘,并分配到所在分区的对应的 4 个回放任务队列中。
分配方式主要是按照 trans_id 进行哈希,确保同一事务的日志被分配到一个队列中。
异步提交新任务:
被分配到新任务的队列将 task_queue 作为一个任务再次异步提交到全局线程池中等待消费。
消费 task_queue:
当全局线程池消费 task_queue 时,依次遍历队列中所有子任务,并根据日志类型执行相应的应用逻辑。
至此,一个日志真正地同步到了 follower 中,在 follower 上也可读。
总结
OceanBase 的事务日志提交和回放流程是一个高度复杂且精细的过程,涉及多个组件和步骤的协同工作。通过实现 Multi-Paxos 协议和乱序日志提交,OceanBase 保证了事务的高可用性和数据的一致性。同时,通过高效的写入和读取机制,以及全局线程池和异步任务队列的使用,OceanBase 实现了日志的高效处理。这些设计和实现使得 OceanBase 能够应对全球部署和大规模分区同步的挑战,为用户提供稳定可靠的事务处理能力。