Skip to content

双写策略是什么

双写策略(Dual Write Strategy)是指在分布式系统中,为了保持多个数据存储(如数据库和缓存,或主从数据库)之间的数据一致性,采用同时写入两个存储的同步操作方式。即在更新数据时,先或同时向两个目标(如 MySQL 和 Redis)写入数据,确保两边数据一致。


1. 双写策略详解

定义

  • 双写策略通常用于解决缓存与数据库一致性问题,或多数据源同步场景。
  • 核心思想:每次写操作都同时更新所有相关存储,避免异步更新带来的延迟或不一致。

实现方式

  1. 先写数据库,后写缓存
  2. 更新数据库 -> 更新缓存。
  3. 先写缓存,后写数据库
  4. 更新缓存 -> 更新数据库。
  5. 同时写入
  6. 并行写入数据库和缓存。

示例(Java + MySQL + Redis)

public void updateUser(String userId, String name) {
    // 写数据库
    userDao.update(userId, name);
    // 写缓存
    redis.set("user:" + userId, name);
}

2. 双写策略的场景

  • 缓存一致性
  • 如 Redis 缓存与 MySQL 数据库同步。
  • 主从同步
  • 主库写完立即同步从库。
  • 异构数据源
  • 如同时更新 ES(ElasticSearch)和数据库。

3. 优点与缺点

优点

  • 一致性强
  • 数据更新实时同步,减少不一致窗口。
  • 简单性
  • 逻辑直观,易实现。
  • 低延迟
  • 无需异步任务,立即生效。

缺点

  • 性能开销
  • 同步写多个存储,增加响应时间。
  • 失败风险
  • 一个存储失败,需回滚或补偿,复杂化事务。
  • 耦合性
  • 业务代码需处理多存储逻辑。

示例问题

  • 更新 MySQL 成功,Redis 失败:
  • 数据不一致,需回滚 MySQL 或重试 Redis。

4. 实现中的优化与解决

(1) 事务保障

  • 用分布式事务(如 XA 或 TCC)保证原子性。
  • 问题:性能低,复杂度高。

(2) 失败重试

  • 一个存储失败时,重试写入。
public void updateUser(String userId, String name) {
    userDao.update(userId, name);
    int retries = 3;
    while (retries-- > 0) {
        try {
            redis.set("user:" + userId, name);
            break;
        } catch (Exception e) {
            Thread.sleep(100); // 重试间隔
        }
    }
}

(3) 异步补偿

  • 双写失败后,记录日志,异步任务修复。
  • 示例:用 MQ 发送补偿消息。

(4) 先删缓存

  • 先删除缓存,写数据库后异步更新缓存,降低不一致性。
redis.del("user:" + userId);
userDao.update(userId, name);

5. 与其他策略对比

  • 双写策略
  • 同步写,强一致,低延迟。
  • 异步写
  • 如 Canal 监听 binlog 更新缓存,高吞吐但有延迟。
  • 读修复
  • 读时检查一致性再修复,适合读多写少。

延伸与面试角度

  • 一致性模型
  • 双写追求强一致性,异步追求最终一致性。
  • 实际应用
  • 电商:库存同步到 Redis 和 MySQL。
  • 日志:写 ES 和数据库。
  • 优化点
  • 分布式锁:防止并发双写冲突。
  • 批量写:减少 IO。
  • 面试点
  • 问“双写问题”时,提失败回滚。
  • 问“替代”时,提 Canal 或读修复。

总结

双写策略通过同步写入多个存储保证数据一致,简单但有性能和失败风险。优化靠事务、重试或异步补偿。面试时,可结合 Redis 示例或提一致性对比,展示理解深度。