尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
在保证线性一致性的情况下如何写Kv
要在 Raft 系统中保证 KV 写操作的线性一致性最核心的原则是RPC 收到Put/Append后不能直接修改 KV也不能在写入 Leader 本地日志后立即返回成功必须等命令被 Raft 提交并应用到 KV 状态机后才能返回OK。一、完整写入流程假设客户端发送Put(x, 100) ClientId C7 RequestId 12完整过程应该是客户端发送写请求 ↓ Leader调用Raft::Start() ↓ 写入Leader本地日志 ↓ 复制给Follower ↓ 多数节点复制成功 ↓ 日志变成Committed ↓ 通过applyChan交给KVServer ↓ KVServer检查请求是否重复 ↓ 真正修改KV数据库 ↓ 更新lastRequestId ↓ 通知等待中的RPC线程 ↓ RPC返回OK对这个项目来说最直观的线性化点就是已提交命令在GetCommandFromRaft()中真正应用到 KV 状态机的时刻。二、RPC线程不能直接写KVPutAppend()收到请求后只负责构造命令Op op; op.Operation args-op(); op.Key args-key(); op.Value args-value(); op.ClientId args-clientid(); op.RequestId args-requestid();然后提交给 Raftm_raftNode-Start(op, raftIndex, term, isleader);这里的Start()通常只表示Leader接受了命令并尝试把它追加到Raft日志它不代表命令已经得到多数节点确认 命令已经提交 KV数据库已经修改因此不能这样写m_raftNode-Start(...); reply-set_err(OK); // 错误此时还没有提交否则 Leader 可能刚写入本地日志就宕机命令后来被新 Leader 覆盖但客户端已经收到成功线性一致性就被破坏了。三、真正修改KV的位置项目是在收到 Raft 的ApplyMsg后修改 KVvoid KvServer::GetCommandFromRaft(ApplyMsg message) { Op op; op.parseFromString(message.Command); if (!ifRequestDuplicate(op.ClientId, op.RequestId)) { if (op.Operation Put) { ExecutePutOpOnKVDB(op); } if (op.Operation Append) { ExecuteAppendOpOnKVDB(op); } } SendMessageToWaitChan(op, message.CommandIndex); }对应代码[kvServer.cpp (line 166)](C:/Users/LENOVO/Desktop/KVstorageBaseRaft-cpp-main/src/raftCore/kvServer.cpp:166)这里先去重再执行Raft已经提交 ↓ 检查是否重复 ↓ 修改KV ↓ 通知RPC线程这才是正确的写入路径。四、图片中的timeOutPop()是什么RPC线程把命令交给 Raft 后会通过日志下标找到对应的等待队列chForRaftIndex-timeOutPop( CONSENSUS_TIMEOUT, raftCommitOp );它在等待Raft 应用线程通知我这个日志位置上的命令已经提交并应用了。这里存在两种结果。情况一等待到了Apply消息也就是进入图片的elseif (raftCommitOp.ClientId op.ClientId raftCommitOp.RequestId op.RequestId) { reply-set_err(OK); } else { reply-set_err(ErrWrongLeader); }为什么不能只看到相同的raftIndex就返回成功假设旧 Leader 在日志位置 10 写入index10客户端C7的请求12但还没提交旧 Leader 就失去领导权。新 Leader可能用另一条命令覆盖这个位置index10客户端C9的请求20此时 RPC线程等到了index10的应用通知但应用的不是自己的命令。所以必须检查raftCommitOp.ClientId op.ClientId raftCommitOp.RequestId op.RequestId只有两者都相同才能证明被应用的确实是当前客户端的当前请求然后才能返回OK。五、情况二等待超时图片中的代码是if (!chForRaftIndex-timeOutPop(...)) { if (ifRequestDuplicate(op.ClientId, op.RequestId)) { reply-set_err(OK); } else { reply-set_err(ErrWrongLeader); } }超时只表示RPC线程在规定时间内没有收到Apply通知不表示命令一定失败。命令可能已经成功应用只是Apply通知到达得比较晚 等待队列通知丢失 RPC线程恰好先超时 网络或线程调度发生延迟因此超时后再次检查ifRequestDuplicate(op.ClientId, op.RequestId)它的判断逻辑是return RequestId m_lastRequestId[ClientId];已经执行过如果返回truelastRequestId[C7] 12说明状态机已经执行过这个请求所以可以返回reply-set_err(OK);注意这不是“把重复请求重新执行一次”而是请求已经执行过这次只补发成功响应尚未确认执行如果返回false只能说明目前没有证据证明请求已经应用不能确定它最终会不会提交。因此不能返回OK而是返回一个可重试错误reply-set_err(ErrWrongLeader);这里的ErrWrongLeader不一定真的表示“节点不是 Leader”更多是在告诉客户端当前执行结果不确定请使用相同的ClientId RequestId重试。六、客户端怎么重试客户端创建一个新逻辑请求时只增加一次RequestIdm_requestId; auto requestId m_requestId; while (true) { args.set_clientid(m_clientId); args.set_requestid(requestId); // 不断尝试不同节点 }比如第一次发送(C7, 12) 超时后重试(C7, 12) 换Leader重试(C7, 12)不能变成第一次发送(C7, 12) 第一次重试(C7, 13) 第二次重试(C7, 14)否则服务端会把它们当成三个不同操作导致Append重复执行。七、KV和去重表必须一起更新执行Put时void KvServer::ExecutePutOpOnKVDB(Op op) { m_mtx.lock(); m_skipList.insert_set_element(op.Key, op.Value); m_lastRequestId[op.ClientId] op.RequestId; m_mtx.unlock(); }这里同时更新KV数据 lastRequestId去重信息这是必要的。不能出现KV已经修改 但lastRequestId没有更新否则同一个请求重试时会被再次执行。逻辑上它们应该是状态机的一次原子状态转换(KV状态, 去重状态) ↓ 同时应用一条已提交命令 ↓ (新KV状态, 新去重状态)八、这段代码保证成功写入的依据只有下面两种情况能够返回OK1. 等到了Apply消息并且ClientId、RequestId都匹配 2. 等待超时但去重表证明这个请求已经应用过以下情况不能返回成功刚调用Start() 只写入了Leader本地日志 只知道自己目前是Leader 等待到相同日志下标但不是相同请求 超时且去重表里找不到请求
RELATED

相关推荐

Raft如何做到线性一致性

Raft如何做到线性一致性

一、为什么需要请求去重考虑一次 Append("name", "A"):1. 客户端发送 Append 请求 2. Leader 将请求提交到 Raft 日志 3. KV 状态机执行成功,name 变成 "A" 4. 服务端返回响应 5. 但响应在网络中丢失 6. 客户端超时&#…

📅 2026/8/21 4:16:54
线性一致性

线性一致性

线性一致性(Linearizability)**是分布式系统中的一种强一致性模型。一句话定义:所有操作看起来都像是在某个唯一时间点瞬间完成,并且这个时间点必须位于操作发起和响应之间,同时不能违背现实时间顺序。核心要求假设有多…

📅 2026/8/21 4:17:09
赏金不买漏洞——四个关于黑客价值的观点

赏金不买漏洞——四个关于黑客价值的观点

一句话定位赏金不是奖励,是赎金——厂商买的不是漏洞,是漏洞持有人本来可以造成的灾害。先讲清楚一个前提:黑客的能力,永远以破坏能力为尺度。无论破坏是否已经发生。发现一个能瘫痪全国电网的漏洞,和实际上有没有去瘫…

📅 2026/8/21 4:17:10
MORE NEWS

更多资讯

📰

Matter tv-app Android Common-API 模块详解:内容应用与 Matter Agent 服务的 AIDL 跨进程通信机制

Matter tv-app Android Common-API 模块详解:内容应用与 Matter Agent 服务的 AIDL 跨进程通信机制 【免费下载链接】connectedhomeip Matter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers …

📰

ASP.NET在线考试系统:组卷、交卷并发与防作弊设计

简介:这份资源是一份基于ASP.NET的在线考试系统设计与实现文档,面向计算机相关专业的毕业设计学生、课程设计开发者以及需要搭建B/S架构考试平台的入门与中级技术人员。文档围绕在线考试的实际需求展开,完整梳理了系统从研究背景、可行性分析…

📰

Hello-Agents 共创实战:用 Reflection 反思机制构建 CodePlanAgent 智能代码规划工具

Hello-Agents 共创实战:用 Reflection 反思机制构建 CodePlanAgent 智能代码规划工具 【免费下载链接】hello-agents 📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程 项目地址: https://gitcode.com/datawhalechina/hello-agents …

📰

IEEE 802.11a/g物理层OFDM链路级仿真:从发射机到误码率统计的完整实现

我做过不少无线通信的链路级仿真,但最常被学生问到的一个问题始终是:“书上写的OFDM流程我都懂,为什么自己写代码跑出来BER曲线就是不对?”这个问题背后,其实藏着一个很现实的需求——缺一套足够贴近标准、结构清晰、能…

📰

Linux安装为何必须挂载/boot/efi:UEFI引导与ESP分区详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

从 .doc 到结构化题库:选择题文档解析与 JSON 转换全流程

简介:这份习题集为计算机专业基础课程提供了典型选择题训练,面向本科、高职、自考等阶段的初学者与备考者,可用于章节自测或考前速记。文档以1个doc文件打包,整体仅415KB,轻量便于下载后打印或导入笔记软件使用。全部题…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬