TiKV Raft日志复制源码深度解析

一、Leader端的AppendEntries实现

1. 核心处理流程

LeaderFollower接收客户端请求追加到本地日志发送AppendEntries响应结果提交日志更新CommitIndex回退NextIndex重发日志alt[多数节点成功][失败]LeaderFollower

2. 源码实现(raftstore/src/store/peer_msg_handler.rs)

impl PeerMsgHandler {
    fn propose_raft_command(&mut self, req: RaftCmdRequest) {
        // 1. 构建日志条目
        let mut entry = Entry {
            entry_type: EntryType::EntryNormal,
            data: req.write().to_bytes(),
            ..Default::default()
        };
        
        // 2. 追加到Leader本地日志
        let last_index = self.raft_group.raft.append_entry(&mut [entry]);
        
        // 3. 构造AppendEntries消息
        for peer in self.peers.keys() {
            if *peer == self.peer_id() { continue; } // 跳过自己
            
            // 获取该follower的next_index
            let next_idx = self.raft_group.raft.prs().get(*peer).next_idx;
            
            // 获取需要发送的日志
            let ents = self.raft_group.raft.raft_log.entries(next_idx, None);
            
            // 构建PrevLog信息
            let prev_index = next_idx - 1;
            let prev_term = self.raft_group.raft.raft_log.term(prev_index);
            
            // 构建AppendEntries消息
            let msg = Message {
                msg_type: MessageType::MsgAppend,
                to: *peer,
                term: self.raft_group.raft.term,
                log_term: prev_term,
                index: prev_index,
                entries: ents.into(),
                commit: self.raft_group.raft.raft_log.committed,
                ..Default::default()
            };
            
            // 4. 发送消息
            self.transport.send(msg);
        }
    }
}

3. 响应处理逻辑

impl PeerMsgHandler {
    fn handle_raft_message(&mut self, msg: Message) {
        match msg.msg_type {
            MessageType::MsgAppendResponse => {
                if msg.reject {
                    // 日志不一致处理
                    self.handle_reject_append(msg);
                } else {
                    // 成功响应处理
                    self.handle_success_append(msg);
                }
            }
            // ...其他消息类型
        }
    }

    fn handle_success_append(&mut self, msg: Message) {
        let peer_id = msg.from;
        
        // 更新匹配索引
        let pr = self.raft_group.raft.prs().get_mut(peer_id);
        pr.matched = msg.index;
        pr.next_idx = msg.index + 1;
        
        // 尝试推进提交索引
        self.raft_group.raft.advance_commit_index();
    }

    fn handle_reject_append(&mut self, msg: Message) {
        let peer_id = msg.from;
        
        // 回退next_index
        let pr = self.raft_group.raft.prs().get_mut(peer_id);
        pr.next_idx = std::cmp::max(1, pr.next_idx - 1);
        
        // 立即重试
        self.send_append(peer_id);
    }
}

二、Follower端的一致性检查

1. 一致性检查逻辑

接收AppendEntries
PrevLogIndex存在?
PrevLogTerm匹配?
返回失败
追加新日志
返回失败
更新CommitIndex
返回成功

2. 源码实现(raft-rs/src/raft/log.rs)

impl RaftLog {
    /// 检查日志一致性
    pub fn is_up_to_date(&self, last_index: u64, last_term: u64) -> bool {
        let (term, idx) = self.last_term_index();
        term > last_term || (term == last_term && idx >= last_index)
    }

    /// 查找冲突索引
    pub fn find_conflict(&self, ents: &[Entry]) -> Option<u64> {
        for entry in ents {
            if entry.index >= self.entries.len() as u64 {
                break;
            }
            
            // 检查索引和任期是否匹配
            if self.entries[entry.index as usize].term != entry.term {
                return Some(entry.index);
            }
        }
        None
    }
}

3. Follower处理逻辑(raft-rs/src/raft.rs)

impl Raft {
    fn step_follower(&mut self, mut m: Message) -> Result<()> {
        match m.get_msg_type() {
            MessageType::MsgAppend => {
                // 1. 检查Leader合法性
                if m.term < self.term {
                    return self.send_reject_append(m.from);
                }
                
                // 2. 重置选举超时
                self.reset_election_timeout();
                
                // 3. 检查PrevLogIndex
                if m.index > self.raft_log.last_index() {
                    return self.send_reject_append_hint(m.from, self.raft_log.last_index());
                }
                
                // 4. 检查PrevLogTerm
                if let Some(local_term) = self.raft_log.term(m.index) {
                    if local_term != m.log_term {
                        // 发现冲突,返回冲突索引
                        let conflict_idx = self.raft_log.find_conflict_index(m.index, m.log_term);
                        return self.send_reject_append_hint(m.from, conflict_idx);
                    }
                } else {
                    // 本地没有该索引的日志
                    return self.send_reject_append_hint(m.from, self.raft_log.last_index());
                }
                
                // 5. 追加新日志(覆盖冲突部分)
                if let Some(conflict_idx) = self.raft_log.find_conflict(&m.entries) {
                    // 截断冲突点之后的日志
                    self.raft_log.truncate(conflict_idx);
                    
                    // 追加新日志
                    for entry in m.entries.iter().skip((conflict_idx - m.index) as usize) {
                        self.raft_log.append_entry(entry.clone());
                    }
                } else {
                    // 直接追加
                    for entry in m.entries {
                        self.raft_log.append_entry(entry);
                    }
                }
                
                // 6. 更新CommitIndex
                if m.commit > self.raft_log.committed {
                    self.raft_log.commit_to(min(m.commit, self.raft_log.last_index()));
                }
                
                // 7. 返回成功
                self.send_append_response(m.from, false);
            }
            // ...其他消息类型
        }
        Ok(())
    }
    
    fn send_reject_append_hint(&mut self, to: u64, hint: u64) {
        let mut msg = Message::new();
        msg.set_msg_type(MessageType::MsgAppendResponse);
        msg.to = to;
        msg.reject = true;
        msg.reject_hint = hint;
        self.send(msg);
    }
}

三、关键数据结构

1. 日志条目结构(raft-rs/src/raftpb/raft.proto)

message Entry {
    optional EntryType entry_type = 1;
    optional uint64 term = 2;
    optional uint64 index = 3;
    optional bytes data = 4;
    optional bytes context = 5;
    optional uint64 sync_log = 6;
}

2. 复制状态(raft-rs/src/raft/progress.rs)

pub struct Progress {
    // 下一个要发送的日志索引
    pub next_idx: u64,
    
    // 已匹配的最高日志索引
    pub matched_idx: u64,
    
    // 复制状态(Probe/Replicate/Snapshot)
    pub state: ProgressState,
    
    // 暂停状态(流量控制)
    pub paused: bool,
    
    // 待发送的日志数量
    pub pending_queue: Vec<u64>,
    
    // RTT测量
    pub recent_active: Instant,
}

四、高级优化技术

1. 批量日志处理

impl PeerMsgHandler {
    fn batch_append_entries(&mut self) {
        // 批量收集待发送日志
        let mut batch = Vec::new();
        for _ in 0..BATCH_SIZE {
            if let Some(entry) = self.log_queue.pop() {
                batch.push(entry);
            } else {
                break;
            }
        }
        
        // 批量发送
        if !batch.is_empty() {
            self.append_entries_batch(batch);
        }
    }
}

2. 日志复制流水线

Batch 1
Batch 2
Batch 3
ACK 1
ACK 2
ACK 3
Leader
Follower

3. 流量控制(raft-rs/src/raft/progress.rs)

impl Progress {
    pub fn maybe_update(&mut self, n: u64) -> bool {
        if self.matched_idx < n {
            self.matched_idx = n;
            self.next_idx = n + 1;
            
            // 根据网络状况调整状态
            if self.state == ProgressState::Probe {
                self.state = ProgressState::Replicate;
                self.paused = false;
            }
            
            return true;
        }
        false
    }
    
    pub fn resume(&mut self) {
        // 根据网络RTT动态调整窗口大小
        let rtt = self.recent_active.elapsed();
        let inflight = min(INIT_INFLIGHT + (rtt.as_millis() / 10) as usize, MAX_INFLIGHT);
        
        self.paused = self.pending_queue.len() >= inflight;
    }
}

五、容错处理机制

1. 日志冲突解决

impl RaftLog {
    pub fn find_conflict_index(&self, index: u64, term: u64) -> u64 {
        // 二分查找冲突点
        let mut low = self.committed + 1;
        let mut high = min(index, self.last_index());
        
        while low <= high {
            let mid = (low + high) / 2;
            let mid_term = self.term(mid).unwrap();
            
            if mid_term == term {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        
        low // 返回第一个冲突索引
    }
}

2. 快照处理流程

LeaderFollowerInstallSnapshot请求保存快照重置日志快照响应继续发送日志LeaderFollower

3. 网络分区恢复

impl Raft {
    fn handle_leader_inactive(&mut self) {
        // 检测Leader失活
        if self.election_elapsed > self.randomized_election_timeout {
            // 发起预投票
            self.pre_vote();
            
            // 收到多数响应后转为Candidate
            if self.pre_vote_granted() {
                self.become_candidate();
            }
        }
    }
}

六、性能优化实践

1. 写路径优化

// 使用零拷贝技术
fn append_entry(&mut self, entry: Entry) {
    // 避免深度拷贝
    self.entries.push(EntryRef::Borrowed(&entry));
    
    // 异步持久化
    self.persist_task.send(entry);
}

2. 读路径优化(Lease Read)

impl Lease {
    pub fn validate(&self) -> bool {
        // 检查租约是否有效
        let now = Instant::now();
        now < self.expire_time && 
        now - self.last_update < MAX_CLOCK_DRIFT
    }
}

fn local_read(&self, req: ReadRequest) -> ReadResponse {
    if self.lease.validate() {
        // 本地读取
        self.storage.get(req.key)
    } else {
        // 走ReadIndex流程
        self.read_index(req)
    }
}

3. 内存优化

// 日志压缩
fn compact_log(&mut self, compact_index: u64) {
    // 保留最近1000条日志
    let retain_count = min(self.entries.len(), 1000);
    let start_index = self.entries[0].index;
    
    if compact_index > start_index + retain_count as u64 {
        // 截断日志
        self.entries.drain(0..(compact_index - start_index) as usize);
        
        // 更新索引偏移
        self.offset = compact_index;
    }
}

七、工程实践总结

TiKV的Raft日志复制实现包含以下关键创新:

  1. 多层次流水线

    客户端请求
    批量打包
    网络发送
    并行应用
    异步持久化
  2. 动态流量控制

    • 基于RTT的自适应窗口调整
    • 拥塞避免算法
    • 优先级队列
  3. 混合一致性模型

    match consistency_level {
        Consistency::Linearizable => self.read_index(),
        Consistency::LeaseBased => self.local_read(),
        Consistency::Stale => self.lease.stale_read(),
    }
    
  4. 性能优化矩阵

    场景优化技术效果提升
    日志存储批量追加30%写入吞吐↑
    网络传输流水线50%延迟↓
    冲突处理二分查找90%冲突定位时间↓

关键源码路径:

  • 日志复制核心:raftstore/src/store/peer_msg_handler.rs
  • 状态机应用:raftstore/src/store/peer.rs
  • Raft算法实现:raft-rs/src/raft.rs
  • 日志管理:raft-rs/src/log.rs
  • 复制状态:raft-rs/src/progress.rs

TiKV通过深度优化Raft日志复制,实现了单Region 10万+ QPS的吞吐能力,同时保证强一致性,成为分布式存储系统的典范实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.Shu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值