双写模型是零停机迁移的必要条件而非充分条件;需业务代码显式控制双写路径、解决结构映射、精准对齐oplog起始点,并统一错误处理与监控。

双写模型本身不能自动消除停机时间,它只是零停机迁移的必要条件之一;真正决定是否“零停机”的,是双写启动时机、数据一致性校验机制和最终切流策略的配合程度。
双写必须在应用层显式控制写入路径
很多人误以为加个中间件或代理就能自动双写,实际不是。MongoDB没有原生双写开关,所有双写逻辑必须由业务代码主动发起。这意味着:
-
save()、insert_one()、update_one()等操作需拆分为两路:一路写旧库(MongoDB),一路写新库(如金仓KES或MySQL) - 不能依赖连接池或驱动层自动转发,否则无法处理字段映射、类型转换、错误隔离等关键问题
- 推荐封装一个
SafeDualWriter工具类,统一管理写入成功判定逻辑——仅当两库都返回成功才视为本次写入完成 - 若新库写入失败,需记录失败日志+降级为单写,并触发告警,避免静默丢数据
双写期间必须解决文档→关系型结构映射问题
从MongoDB迁出时,最常踩的坑不是同步延迟,而是字段语义错位。例如:
- MongoDB中
user.profile.address是嵌套对象,金仓KES里得拆成users表 +user_addresses表,且外键约束需手动补全 -
Date类型在MongoDB里是ISODate("2025-03-12T08:30:00Z"),但金仓默认接收的是TIMESTAMP WITH TIME ZONE,不显式转换会报invalid input syntax for type timestamp with time zone - 旧数据里
status字段可能是字符串("active"/"inactive"),新表设计为status_id INT枚举,双写时必须做映射转换,否则插入失败
建议用 KDTS 工具先跑一次结构评估:java -jar kdms-cli.jar --task assess --source-type mongodb --source-host ...,生成字段映射报告再动手改代码。
增量追平阶段最容易被忽略的边界:Oplog 时间窗口漂移
双写启动后,旧库仍有未同步的历史写入(比如备份刚结束、双写尚未上线那几分钟),这部分数据必须靠 Oplog 解析补全。但直接读取 oplog.rs 有陷阱:
- MongoDB 的 Oplog 是循环覆盖的,如果迁移脚本启动太晚,关键变更可能已被覆盖,导致永久性数据丢失
- Oplog 中的
ts字段是Timestamp类型(非 ISO 时间),用 Python 的datetime.fromtimestamp()直接解析会出错,必须用timestamp.time_t提取秒级时间戳 - KFS 同步工具虽支持断点续传,但首次拉取起点必须严格对齐双写开始时刻的
ts,否则会出现重复或跳过
实操建议:双写开启前,先执行 db.oplog.rs.find().sort({$natural:-1}).limit(1) 记下最新 ts 值,作为后续增量同步的起始点。
真正难的不是写两行 insert,而是让两套系统在字段含义、事务边界、错误反馈、监控指标上保持可比性——这些细节没对齐,切流那一刻就不是“平滑”,而是“冒烟”。











