首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Dragonfly 主从复制深度解析:握手、全量/增量同步、稳定态流式与故障切换
📅 2026/9/10 13:00:30
✍️ 爱科研究院
👁 阅读 3,247
Dragonfly 主从复制深度解析握手、全量/增量同步、稳定态流式与故障切换【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonflyDragonfly 是 Redis 与 Memcached 的现代替代品其同构复制Dragonfly 到 Dragonfly在标准 RESP 协议之上构建了一套专有的DFLY子命令体系实现了每分片一条流的高并发同步。本文以仓库 docs/replication.md 为主体结合 src/server/dflycmd.cc、src/server/replica.cc、src/server/journal/journal_slice.cc 等源码完整讲解复制的握手流程、逐流协商、全量快照与增量缓冲、稳定态流式同步、故障切换failover与接管takeover的底层机制以及全部相关配置参数。读完本文你将掌握 Dragonfly 复制状态机的每一处关键节点、部分同步得以成立的原理与边界以及如何用这些参数调优大规模副本同步。两个角色两套状态机复制涉及两个相互独立的状态机它们通过一条控制连接control connection加上每个分片一条的流连接flow进行通信主库侧Master每个已连接副本对应一个会话依次经历PREPARATION - FULL_SYNC - STABLE_SYNC任意状态下都可能进入CANCELLED。副本侧Replica一组逐步累积的标志位connected、greeted、syncing、sync-ok随握手与同步推进逐级置位任何失败都会整体清零重来。主库会把每个副本的同步会话按主库分片数量切分成若干流即每个主库分片一条 TCP 连接副本侧则用每流一个 worker 与之对应。状态名与源码中的SyncState枚举一一对应见 src/server/dflycmd.ccpreparation、full_sync、stable_sync在--info_replication_valkey_compatible开启时显示为online、cancelled。核心术语LSNLog Sequence Number每个分片独立的、单调递增的日志序号标识下一个将被写入的日志条目。LSN不可跨分片比较——每个分片各自维护独立序列。源码中DFLY REPLICAOFFSET正是按分片返回该 LSN 向量src/server/dflycmd.cc。Flow一个分片的复制连接。副本有N个主库分片就有N条流通过 flow-id 映射到分片 id。master-replid主库进程生命周期内随机生成一次的 ID用于让副本判断自己重连的是同一个主库还是不同的主库例如主库重启后。1. 握手每连接问候副本以普通 RESP 客户端连接主库按顺序发送PING——期望PONG。REPLCONF listening-port port——期望OK。REPLCONF ip-address ip仅在设置--replica_announce_ip时发送——尽力而为即使收到错误回复也只记录警告旧版主库可能不支持。REPLCONF capa eof capa psync2——期望OK。REPLCONF capa dragonfly——主库的回复用于区分 Redis 主库单元素OK与 Dragonfly 主库多元素数组。握手的第一步实现在Replica::Greet()中src/server/replica.cc先PING校验PONG再依次发送上述REPLCONF系列命令并依据REPLCONF capa dragonfly回复的元素个数判断对端类型——1 个元素是 Redis3 个及以上是 Dragonfly 主库。主库侧对REPLCONF CAPA dragonfly有特殊处理它会分配一个sync_id为每个分片预留一个流槽位然后回复一个 5 元素数组master_replid sync_id SYNCn num_flows shard count protocol version lineage_id副本解析该回复时Replica::HandleCapaDflyResp()src/server/replica.cc有几处值得注意的行为若广播的master_replid等于副本自身的 client id则拒绝连接防止意外地从自己复制数据。若主库的master_replid与副本上次见到的不一致且设置了--break_replication_on_master_restart则直接中止复制——这是为了防止同地址主库进程重启后带着全新的无关数据导致副本被静默清空。否则丢弃之前记住的每流 LSN强制触发全量重同步部分同步只会在副本上次见过的同一个master_replid下尝试或走下述 故障切换后的部分同步 的特殊路径。第 5 个字段lineage_id会被保存但仅在实验性级联复制特性中使用对应源码中的--experimental_cascaded_partial_sync与sf_-GetLineageId()判断见 src/server/dflycmd.cc若主库未返回该字段则回退为master_replid本身。随后仅在主库是 Dragonfly 时副本继续发送REPLCONF CLIENT-ID id——让主库将会话标记为副本的稳定集群节点 ID对应Replica::ConfigureDflyMaster()中service_.cluster_family().MyID()src/server/replica.cc。REPLCONF CLIENT-VERSION version——副本自身的协议版本存储于会话上后续用于门控部分同步行为与 RDB 特性封装如搜索索引 blob 等。对应DflyVersion::CURRENT_VERsrc/server/replica.cc并记录了在REPLCONF capa dragonfly上回传版本号这一能力见 src/server/version.h。之后副本将自身标记为 greeted。2. 逐流协商DFLY FLOW对num_flows中的每个分片副本新建一条 TCP 连接并发送DFLY FLOW master_repl_id dfly_session_id flow_id [lsn] [last_master_id lsn-vec]lsn仅当副本记得本流在上次断连时、同主库的可续传 LSN且主库广播的版本支持、且开启--replica_partial_sync时才附加。last_master_id lsn-vec上一个主库的 id 与按-连接的每分片 LSN 向量仅当副本记住了自己曾跟随的另一个主库的同步数据见 故障切换后的部分同步且主库版本支持时才附加。解析逻辑见DflyCmd::Flow()src/server/dflycmd.cc其中ParseLsnVec按-切分 LSN 向量src/server/dflycmd.cc。主库侧流处理器会校验master_id不匹配返回bad master id见 src/server/dflycmd.cc、解析flow_id与会话把连接迁移到该分片所属线程conn()-Migrate(...)并惰性初始化该分片的日志环形缓冲缓冲容量默认 8192 条受--shard_repl_backlog_len控制。迁移连接的实现可见SetupFlowConnectionsrc/server/dflycmd.cc它会为新流生成 40 个十六进制字符的随机eof_token。随后主库在会话仍处于PREPARATION状态时决定全量还是部分同步故障切换匹配failover match本主库自身是由被提升的副本演化而来执行过REPLICAOF NO ONE或REPLTAKEOVER并记住了旧主库的 id 与自身已追到的每分片 LSN且请求副本的last_master_id与该记忆中的 id 匹配。此时续传 LSN 取自副本提供的lsn-vec中对应flow_id的值而非lsn参数。源码中的判断为failover_match my_last_master replica_last_master replica_last_master-id my_last_master-idsrc/server/dflycmd.cc。否则如果发送了裸lsn则以其为候选续传点重连同一主库/同一流。候选 LSN 只有在其仍可被取回时才被采纳要么等于当前 LSN什么都没错过要么落在该分片环形缓冲的保留区间内介于缓冲最旧与最新条目之间。若该 LSN 已被缓冲淘汰副本断连太久或缓冲太小主库记录日志并静默回退到该流的全量同步——这种特定失败不会发送错误回复。缓冲内判断逻辑为journal::GetLsn() lsn || journal::IsLSNInBuffer(lsn)src/server/dflycmd.ccIsLSNInBuffer的区间判定见 src/server/journal/journal_slice.cc。主库对DFLY FLOW回复(sync_type, eof_token)其中sync_type为FULL或PARTIALeof_token是全新随机的 40 位十六进制字符串仅在后续全量同步路径中用作 RDB 流的带外结束标记rb-StartArray(2); rb-SendSimpleString(sync_type); rb-SendSimpleString(eof_token);src/server/dflycmd.cc。关键的不对称性全量还是部分同步在DFLY FLOW阶段就按流独立决定——早于副本在控制连接上发出DFLY SYNC。每条流的答案在DFLY SYNC执行前就已定死DFLY SYNC会扇出到各分片对任何已协商为PARTIAL的流完全跳过启动全量同步 saver绝不会重新审视DFLY FLOW的决定src/server/dflycmd.cc 中若流已带start_partial_sync_at值则直接返回INVALID_VALUE拒绝DFLY SYNC。不过逐流决定不一定是会话的最终结果若各流意见不一有的全量、有的部分这种不一致会在之后才被发现并强制整个会话回退到全量重同步见下文混合全量/部分。3. 全量同步DFLY SYNC一旦所有流都回复了DFLY FLOW副本就在第一条控制连接上发送DFLY SYNC sync_id。主库要求会话处于PREPARATION状态并在事务保护Transaction::Guard确保没有写事务处于飞行途中下为每个尚未解析为PARTIAL的分片启动全量快照随后将会话转入FULL_SYNC并回复OK——不等待快照本身完成RDB 字节会在已打开的流 socket 上异步流出。DflyCmd::Sync()的实现确认了这一流程src/server/dflycmd.cc先校验状态为PREPARATION然后shard_set-RunBlockingInParallel对每个分片执行StartFullSyncInThread全部成功后将会话置为FULL_SYNC并SendOk()。每个分片启动全量同步时创建直接写入流 socket 的 RDB saver。分片 0 还会额外保存摘要Lua 脚本、全局元数据、搜索索引定义连同自身数据其余分片只保存自己分片的数据。开始桶遍历快照。关键点当请求了日志流式时快照在开始遍历哈希表之前就先把自身注册为日志消费者——早于遍历 fiber 的启动。任何落在快照游标已越过之后的键/桶上的写入都会被捕获为日志条目并追加进同一条 RDB 字节流中与桶数据交错。这正是客户端在全量同步期间可以持续写入的安全保证快照到日志之间没有交接空窗因为日志监听器在整个快照期间都处于活跃状态而不只是快照之后。所有桶遍历完成后快照在 RDB 流中写入全量同步截断标记full sync cut marker。副本侧每条流把 socket 喂给 RDB loaderloader 带一个回调首次观察到截断标记时递减共享计数器。副本会阻塞到每条流都命中截断标记——这样副本就能知道所有分片上的 RDB照片部分已同时完成尽管每个分片是独立、按各自节奏流式的。截断标记之后主库的快照并未结束日志变更仍会继续转发进 RDB 流。脱离该阶段由DFLY STARTSTABLE下一节单独驱动——结束全量同步会把日志偏移作为 RDB opcode 发送副本侧读回、flush然后才把原始eof_token字节写到 socket。副本的全量同步 worker 在把流的全量同步视为完成前会从线上读取并校验该eof_token并把越过 token 读取到的多余字节暂存起来作为稳定态同步流的起始部分重放连接足够快时token 之后可能已经跟着实时日志字节。若每条流都协商为PARTIAL以上全部不会发生同步类型短路为partial副本直接跳到发送DFLY STARTSTABLE。同一会话内各流全量/部分混杂被视为不可恢复不会做部分同步部分流必须全量重同步——该会话的复制报错并从头重连。副本清空现有数据集只发生一次在启动全量流之前、且仅当所有流都解析为全量时——纯部分重同步绝不会触碰现有数据。桶序列化、加锁与并发写入快照遍历与实时客户端写入在无全局锁的情况下并发作用于同一张哈希表。该机制全量同步、SAVE/BGSAVE、集群槽位迁移共用是基于每桶写时复制per-bucket copy-on-write的方案遍历 fiber 按物理哈希表桶顺序行走。每个桶都有版本号快照在开始时记住目标版本。桶只有在其版本仍旧于目标版本时才会被序列化且会在序列化之前打上目标版本戳因此并发二次访问——无论是遍历继续推进还是写入与之竞争——都保证是空操作。写入不会被快照阻塞。数据库层会在每次即将触碰某个桶时、在应用变更之前通知快照。该通知内联在写入者自己的 fiber 上执行相同的未访问则序列化逻辑side-save捕获变更前的值并标记桶已完成让遍历稍后跳过。若桶已被序列化则只是做一次版本检查、零额外工作。无论哪种情况都是由调用写入的 fiber 自己完成这些工作后继续执行变更触碰其他桶的其他客户端 fiber 永远不会因此被阻塞因为每个桶的版本/闩状态相互独立——这条路径上没有任何表级锁。路径上唯一的真正互斥锁保护的是输出流共享序列化器的缓冲/socket 写入器而非桶访问它阻止遍历 fiber 正在写入的大值与另一个 fiber 的 side-save 变更在同一输出流上交错、使值被拦腰截断。仅当关闭 tag-chunk 序列化时才真正持有该锁tag-chunk 线上格式通常使这变得不必要。它从不用于门控桶访问。对卸载到分层存储tiered storage的值每个桶通过一个小的每桶闩跟踪进行中的异步取回因此对该特定桶的二次触碰例如遍历到达时 side-save 仍在等待分层读取完成只会阻塞到该桶的挂起读取解析——同样是桶级而非全局级。该机制保证的不变式对于任何键副本/快照必须严格在变更它的日志条目之前观察到变更前的值且上述机制做到这一点而无需阻塞无关键的写入。限速快照全量同步以及普通SAVE/BGSAVE的出口流量按分片线程限速由--snapshot_egress_limit_bytes字节/秒0表示不限制为默认值配置。它使用 GCRA通用信元速率算法决定遍历 fiber 必须睡眠多久才能把观测字节速率控制在限制之下。该标志在 src/server/server_state.cc 中定义为strings::MemoryBytesFlag默认值 0不限制并通过egress_throttler_.SetLimit(...)生效。两个调用点协同工作桶遍历循环每次迭代请求一次限速若该分片当前超预算则阻塞实际写入路径在数据推出时记录字节数。限速器区分高、低优先级出口遍历 fiber 自身推送的常规批量快照数据记为低优先级而任何其他 fiber 推送的数据如响应实时写入的内联 side-save记为高优先级且在低优先级出口已占用其基线份额之前不会被限速。这保证了大批量快照不会饿死同一条连接上搭载的实时日志流量反之亦然——饱和的快照出口预算只会拖慢遍历而不会拖慢普通写入。同一个每线程限速器实例也被稳定态同步的日志流式器与集群迁移的桶循环使用因此--snapshot_egress_limit_bytes实际限制的是每个分片线程的总复制/迁移出口带宽而不只是最初的全量快照照片。4. 稳定态同步DFLY STARTSTABLE副本在每条流上都观察到截断标记后或全部分支的部分同步会话立即发送DFLY STARTSTABLE sync_id。主库要求会话处于FULL_SYNC或PREPARATION状态后者覆盖从未发送DFLY SYNC的全部分支部分同步情形且每条流的连接仍然存活。对每个分片若该流是全量结束快照发送日志偏移与 EOF token如前所述。若该流是部分无需停止任何东西从未启动 saver。无论哪种情况在流 socket 上启动实时日志流式器。DflyCmd::StartStable()的实现确认了上述逻辑src/server/dflycmd.cc校验状态后并行执行非部分流则StopFullSyncInThread然后StartStableSyncInThread最终将会话置为STABLE_SYNC。启动日志流式器若这是全量同步流总是从现在开始稳定态同步立即注册为实时日志消费者。若是起始 LSN 非零的部分同步流则暂不注册——后台写入者先从请求的 LSN 开始逐条正向遍历环形缓冲把每条直接写到 socket追平当前 LSN 后才注册为实时消费者。若重放期间缓冲条目在其脚下被淘汰正常情况下不该发生因为淘汰检查在DFLY FLOW时已通过但缓冲仍在并发推进它会报告不可恢复错误而非静默重同步。会话转入STABLE_SYNC主库回复OK。副本侧每条流启动一对读/ACK worker 并阻塞直到错误/取消将其全部拆除——没有干净退出稳定态同步的方式唯一出口是取消后的错误。线上格式与命令分组每条日志条目携带一个 opcodeSELECT、EXPIRED、COMMAND、PING、LSNCOMMAND/EXPIRED还附带事务 id、数据库索引与背书的命令参数。副本把原始流逐个转成按事务组织的记录并在跟踪 LSN 时把 LSN opcode 内嵌值与自身运行计数器交叉校验不匹配时记日志而非失败。跨分片事务全局命令FLUSHALL/FLUSHDB/DFLYCLUSTER FLUSHSLOTS以及以共享事务 id 标识的普通跨分片 multi 命令会在各流 worker 间重新同步每条流把自己的事务 id 插入共享映射等待所有参与分片都收到自己的部分然后一个屏障确保恰好一条流最先插入的那条真正执行一次全局命令同时所有流再次在屏障上等待其完成后再继续。这是必要的因为每条流 worker 独立读取执行、与其他流并不同步。PING日志条目主库用来强制 ACK见 背压与 ACK 与 接管会计入副本已执行记录数但不触碰事务执行器且若本副本分片自己的日志恰好处于活跃状态也会被重新记录进其中。背压与 ACK每条流运行一个 ACK worker周期性--replication_acks_interval默认 1000ms或在新增执行记录数 1024时立即在同一条流 socket 上向主库发送REPLCONF ACK executed_count。主库记录每条流最后 ACK 的 LSN——该写入发生在拥有该分片流连接的线程上因此绝无跨线程访问。标志定义见 src/server/replica.ccreplication_acks_interval默认 1000ms。主库侧日志流式器在挂起写缓冲达到--replication_stream_output_limit默认 1MB时阻塞写日志的 fiber而非副本连接直到进行中的 socket 写排空最多等待--replication_timeout默认 30000ms超时即放弃并带错误取消整个复制会话。这是主库对慢速或卡死副本的推回机制——它是不对称的落后的副本会让主库该分片上的所有写者停摆而不只是一条连接。相关标志定义见 src/server/journal/streamer.ccreplication_timeout默认 30000ms与同文件 L32 处replication_stream_output_limit默认 1MB。检测卡死的全量同步独立于 ACK主库周期性每分片心跳会检查本分片上仍持有活跃全量同步 saver 的每个副本若该 saver 的最后写入时间早于--replication_timeout则强制取消该副本的整个会话。这只适用于全量同步阶段全量同步结束后 saver 引用被清除稳定态同步依赖日志流式器自身的背压超时。部分同步缓冲及其边界部分同步的近期历史是每个分片的环形缓冲容量由--shard_repl_backlog_len控制默认 8192 条——是条数而非字节数。它由每次日志写入填充无论当前是否有副本连接分片的日志记录在首个副本附着时启动值得注意的是当本节点从主库被降级为某人的副本、后来又恢复作为数据源时也会启动。副本重连时若请求的 LSN 已被淘汰出缓冲或缓冲被显式清空由强制所有副本全量同步触发则受影响流总是回退到全量同步——不存在部分的部分同步一条流要么完全可续传要么必须完全重同步。故意清空缓冲会把 LSN 计数器推进到越过当前缓冲内容之上正是为了让一个过期副本持有的 LSN 永远不可能在已清空的缓冲里意外地别名化为未来的 LSN、从而被授予一个基于它其实从未见过的数据的虚假部分同步。值得注意的当前仓库实现细节shard_repl_backlog_len在 src/server/journal/journal_slice.cc 中已被标记为 legacy 标志默认值实际为 0默认路径改用 8192 条容量加上基于时间/字节的淘汰策略shard_repl_backlog_time_ms默认 5000ms 保留时长与shard_repl_backlog_max_bytes默认 0此时按maxmemory / shard count / 200推导见 src/server/journal/journal_slice.cc 与JournalSlice::Init()同文件 L79-L97。只有当用户显式配置了非零的--shard_repl_backlog_len且未配置新式标志时才回到纯条数上限模式并打印弃用警告同文件 L84-L92。换言之在源码层面8192 条是当前默认缓冲容量而旧文档中的--shard_repl_backlog_len8192这一表述在现行版本中对应的是这条默认路径。部分同步提升与故障切换当副本被提升为主库时其他原本跟随旧主库的副本仍可通过部分同步恢复而不是全量重同步走的就是流协商中上述故障切换匹配failover match的不同主库路径。提升通过REPLICAOF NO ONE或REPLTAKEOVER/DFLY TAKEOVER完成。两者最终都会停止本地副本角色——若它已达到稳定态同步则会返回旧主库的 id 与本节点已执行的每流 LSN 快照暂存备用。其他跟随旧主库的副本在自己停止/重连路径上也会发送各自记住的last_master_id/lsn-vec若匹配它们就能在新主库相同的日志序列中从自己最后见过的 LSN 续传因为被提升的节点会延续其作为副本时的日志 LSN 编号而不是重置为 1 或新的 epoch。该 LSN 连续性来自重启每个分片的日志时起始编号为该节点实际为本分片映射流执行的日志记录数至少 1因为日志编号从 1 开始——而不是 0。对应实现为Replica::StartJournalAtOwnLSN()src/server/replica.cc它调用journal::StartInThreadAtLsn(rec_executed)每条流的已执行计数由GetRecCountExecutedPerShard计算且日志总是从 1 开始同文件 L1517-L1519。这发生在两条提升路径上REPLICAOF NO ONE由--replicaof_no_one_start_journal默认true门控——设置后保留REPLICAOF NO ONE之后的日志偏移。实现见 src/server/server_family.cc 与 L3430 处的调用点。REPLTAKEOVER无条件执行且在向旧主库发出接管 RPC之前——这样本节点的日志在接管完成、它成为主库的那一刻就已温热、部分同步就绪。调用点见 src/server/server_family.cc。5. 接管REPLTAKEOVER/DFLY TAKEOVERREPLTAKEOVER seconds [SAVE]在副本上发出它会作为DFLY TAKEOVER seconds [SAVE] sync_id转发给主库。主库侧在持有请求会话的共享锁并要求其已处于STABLE_SYNC的前提下原子地翻转主库全局状态ACTIVE - TAKEN_OVER——这是实际的封锁并发的第二次接管尝试在此失败对应sf_-service().SwitchState(GlobalState::ACTIVE, GlobalState::TAKEN_OVER)src/server/dflycmd.cc。在给定超时内等待所有监听器上飞行中的命令分发全部完成并禁用键过期使交接窗口内没有任何状态变更src/server/dflycmd.ccDispatchTracker等待 SetExpireAllowed(false)失败则翻转回ACTIVE并返回Takeover failed!。仅对请求接管的副本发送日志PING强制每个稳定态同步流立即发送最新 ACK而非等待正常间隔并忙等每个分片最后 ACK 的 LSN 等于主库当前 LSN——即副本现已应用了全部数据。实现为WaitReplicaFlowToCatchup(..., with_pingtrue)src/server/dflycmd.cc。若成功对接管请求回复OK然后——尽力而为不强制 PING强制 PING 本身会推进 LSN、破坏这些节点的部分同步——等待所有其他已连接副本也追平使它们不会错过数据或需要对即将成为新主库的副本做全量重同步src/server/dflycmd.cc。可选地执行同步SAVE仅测试用的旋钮然后关闭进程非集群模式或在集群模式下把集群槽位所有权协调到新主库ReconcileMasterSlotssrc/server/dflycmd.cc。副本侧交接把确认的 OK 变成我现在是主库会在发出接管 RPC之前把自身日志重启到自身最后执行的 LSN这样其日志在成为主库的那一刻就已温热、部分同步就绪src/server/server_family.cc并且只在主库确认OK且旧副本角色拆除之后才翻转为主库模式。6. 取消、断连与清理任何流上的任何错误socket 错误、协议错误、超时都会调用会话的错误处理器会话创建时安装它派生出 worker 停止该sync_id的复制。停止一个会话按顺序执行将会话状态翻转为CANCELLED并取消共享执行上下文——任何阻塞等待它的 fiber 都会解除阻塞并开始回卷。扇出到每个分片执行该流的清理——全量同步流关闭 socket 并取消 RDB saver稳定态同步流关闭 socket 并取消日志流式器。对应ReplicaInfo::Cancel()src/server/dflycmd.cc置CANCELLED、ReportCancelError、并行清理每条流、JoinErrorHandler。汇入错误处理器 worker然后擦除会话并重新发布INFO/指标读取者使用的无锁快照tl_replica_infos每 proactor 缓存见 src/server/dflycmd.cc。副本自身的错误处理类似分层其主循环在任何阶段任何失败时把所有状态标志重置回仅enabled循环等待--master_reconnect_timeout_ms默认 1000mssrc/server/replica.cc后从 DNS 解析开始重试整个握手——任何时点的断连都会从问候重启绝不会中途续接握手。只有是否完成过一次全量同步和最后见过的每流 LSN在停止/重连路径顶部捕获能跨越重连存活且只有后者使重连后的部分同步成为可能。可观测性INFO REPLICATION/DFLYCLUSTER系列工具将会话状态报告为preparation、full_sync、stable_sync若开启--info_replication_valkey_compatible则显示online、cancelled。状态名映射见 src/server/dflycmd.cc该标志默认值为truesrc/server/server_family.cc相应地在INFO中以slave而非replica报告角色字段同文件 L3012。主库侧每副本摘要id、地址、端口、状态、lag由线程本地快照构建在锁下更新并重新发布到每个 proactor读方从不加锁tl_replica_infosUpdateReplicaInfoCacheLocked见 src/server/dflycmd.cc。复制滞后只对处于STABLE_SYNC的副本有意义也仅在该状态计算每个分片为当前 LSN 减去最后 ACK 的 LSN跨分片取最大值。DFLY REPLICAOFFSET返回主库当前的每分片 LSN 向量——WAIT用它来知道副本必须达到的 LSNsrc/server/dflycmd.cc。副本侧为INFO/CLIENT LIST暴露自身视角由状态标志纯粹推导出的人类可读阶段DISABLED/TCP_CONNECTING/GREETING/FULL_SYNC_IN_PROGRESS/INITIAL_SYNC/STABLE_SYNC实现在 src/server/replica.cc 的Replica::GetCurrentPhase()系列逻辑中如STABLE_SYNC分支见同文件 L1501 附近。从 Redis 主库复制若握手时REPLCONF capa dragonfly的回复是单元素OK而非 Dragonfly 主库副本回退到标准 Redis 复制发出PSYNC ? -1解析FULLRESYNC replid offset基于磁盘、带长度前缀的 RDB或 diskless 的EOF:40-byte-token流通过与 Dragonfly 全量同步相同的 RDB loader 加载单分片、无分片流——一切都在一条连接上到达然后解析 Redis 在稳定态同步期间发送的普通 RESP 命令流批量/压缩命令并周期发送REPLCONF ACK offset。部分重同步PSYNCCONTINUE被明确未实现——CONTINUE回复被当作错误partial replication not supported yet因此对 Redis 主库的每次重连都是全量重同步。Dragonfly 不支持反向方向Redis 实例从 Dragonfly 主库复制。相关说明见 src/server/replica.cc 的注释we currently do not support dragonfly-redis replication。配置参数速查以下参数在复制路径中的角色与默认值均以当前仓库源码为准参数默认值作用源码位置--replica_announce_ip空握手时附加REPLCONF ip-address主库不支持仅记警告src/server/replica.cc--break_replication_on_master_restartfalsemaster_replid变化时直接中止复制防止静默清空数据src/server/replica.cc--replica_partial_synctrue是否允许对同主库断连做部分同步src/server/replica.cc--shard_repl_backlog_len0legacy显式配置时生效每分片部分同步缓冲条数上限默认走 8192 条 时间/字节淘汰src/server/journal/journal_slice.cc--shard_repl_backlog_time_ms5000部分同步缓冲条目保留时长毫秒src/server/journal/journal_slice.cc--shard_repl_backlog_max_bytes0按maxmemory / shard 数 / 200推导每分片部分同步缓冲字节上限src/server/journal/journal_slice.cc--snapshot_egress_limit_bytes0不限速每分片线程快照/复制/迁移出口带宽限速GCRAsrc/server/server_state.cc--replication_stream_output_limit1MB日志流式器挂起写缓冲阈值触发写 fiber 背压src/server/journal/streamer.cc--replication_timeout30000ms流式器背压最大等待与全量同步卡死检测阈值src/server/journal/streamer.cc--replication_acks_interval1000ms副本 ACK 周期src/server/replica.cc--master_reconnect_timeout_ms1000ms副本失败后重试握手前的等待src/server/replica.cc--replicaof_no_one_start_journaltrueREPLICAOF NO ONE后保留日志偏移以支持部分同步src/server/server_family.cc--info_replication_valkey_compatibletrueINFO/状态名使用 valkey 兼容措辞online/slavesrc/server/server_family.cc需要强调的是--replication_stream_output_limit、--replication_timeout、--replica_partial_sync等标志还注册在 src/server/main_service.cc 的配置注册表中RegisterMutable意味着它们可在运行时通过CONFIG SET动态调整例如CONFIG SET snapshot_egress_limit_bytes limitsrc/server/rdb_test.cc 的集成测试即演示了该用法。总结Dragonfly 的复制设计以每分片一条流 无全局锁的写时复制快照为核心把全量同步的安全性与实时写入的并发性统一在同一套机制里部分同步依赖每分片 LSN 与环形缓冲的严格边界并借助REPLICAOF NO ONE/REPLTAKEOVER时日志从自身已执行 LSN 继续编号的连续性让故障切换后的副本也能无缝续传。理解这套状态机与参数体系是在生产环境中正确配置副本规模、诊断同步滞后与调优带宽的关键。【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/10 13:00:30
Hydra 手柄失灵?控制器连接、验证与排错完整指南
2026/9/10 12:55:30
YOLOv5行人越界报警系统:基于空间围栏的实时检测与Qt可视化
2026/9/10 12:55:30
Comprehensive Rust 并发实战:用 Mutex + Arc + mpsc 实现无死锁的哲学家就餐问题
2026/9/10 13:45:33
论文降重服务,真的靠谱吗?——从踩坑到建立可控流程的完整指南
2026/9/10 13:45:33
Java构建开源物联网平台的核心架构与实战
2026/9/10 13:45:33
学生成绩预测机器学习系统:从数据清洗到Flask部署全流程
2026/9/10 13:45:33
libcurl 详解 CURLINFO_FILETIME_T:安全获取远端资源的修改时间(64 位时间戳)
2026/9/10 13:45:33
Buck电路双闭环控制模型仿真研究(Simulink仿真实现)
2026/9/10 13:40:33
用 Claude Code 的 /incident 命令建立结构化事故响应:devops-automation 插件实战指南
2026/9/10 0:04:20
AI搜索的信任缺口:企业内容如何在答案时代自证可信
2026/9/10 0:04:20
Spring Boot+Vue+Node.js售后服务系统开发实战
2026/9/10 0:04:20
SpringBoot+Vue民宿预订管理系统开发实践:从架构设计到部署上线
2026/9/10 2:30:52
超人会飞不算本事:系统稳定依赖清晰规则与边界设计
2026/9/10 5:51:31
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
2026/9/10 8:32:02
基于CNN的调制信号识别:MATLAB实现时频图分类实战