TiKV Raft日志复制源码深度解析
一、Leader端的AppendEntries实现
1. 核心处理流程
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. 一致性检查逻辑
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. 日志复制流水线
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. 快照处理流程
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日志复制实现包含以下关键创新:
- 
多层次流水线: 
- 
动态流量控制: - 基于RTT的自适应窗口调整
- 拥塞避免算法
- 优先级队列
 
- 
混合一致性模型: match consistency_level { Consistency::Linearizable => self.read_index(), Consistency::LeaseBased => self.local_read(), Consistency::Stale => self.lease.stale_read(), }
- 
性能优化矩阵: 场景 优化技术 效果提升 日志存储 批量追加 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的吞吐能力,同时保证强一致性,成为分布式存储系统的典范实现。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120340

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号