不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • MySQL

    • 01.安装MySQL
    • 02.MySQL事务
    • 03.MySQL锁
    • 04.MySQL索引 ✅
    • 05.MySQL慢查询
    • 06.MySQL优化
    • 07.binlog redolog undolog ✅
    • 08.MVCC原理 ✅
    • 09.SQL执行过程 ✅
    • 10.MySQL主从同步
    • 11.MySQL主从配置
    • 12.MySQL和Redis一致性
      • 01.如何保证MySQL和Redis一致性
        • 1、先更新MySQL,再更新Redis
        • 2、先更新Redis,再更新MySQL
        • 3、先更新MySQL,再删除Redis
        • 4、先删除Redis,再更新MySQL
        • 5、分布式锁
      • 02.ES和MySQL双写
        • 1、业务逻辑重试机制
        • 2、基于消息队列的解耦
        • 3、双写事务保证(TCC)
        • 4、异步校对和修复机制
        • 5、基于状态标识的方案
    • 13.MySQL查询缓存
    • 90.其他
    • 95.MySQL管理
    • 96.MySQL基本查询
    • 97.创建表结构
    • 98.SQL语句面试50题
    • 99.FAQ
  • Redis

  • Elasticsearch

  • Kafka

  • Etcd

  • MongoDB

  • TiDB

  • RabbitMQ

  • 数据库
  • MySQL
xiaonaiqiang
2024-02-07
目录

12.MySQL和Redis一致性

# 01.如何保证MySQL和Redis一致性

  • 没有完美的方案,只有最适合某场景的方案

  • 这个是数据一致性、系统性能和系统复杂度的选择与取舍

# 1、先更新MySQL,再更新Redis

  • 如果先更新MySQL成功了,还未对Redis进行更新的间隙期,这时如果请求过来,读到的都是Redis的更新前数据

  • 如果先更新MySQL成功了,再更新Redis失败了的话,后面的请求读到的都是Redis的更新前数据,并且后续的补救方案很难做

  • 补救方案一

    • 为Redis更新失败,将MySQL中的对应数据也回滚了,以此达到两者数据的一致性

    • 但MySQL是主数据源,它代表的是数据的“权威性”,这样做显然并不合理

  • 补救方案二

    • 通过Redis重试更新的方式进行补救。但如果重试也失败了,还要继续重试吗?

    • 另外,重试时间间隔设置多少?时间间隔设置长了,影响业务的时间也会变长

    • 时间间隔设置短了,重试成功率又会降低,这些其实都是问题

# 2、先更新Redis,再更新MySQL

  • 这个方案,要比方案一的“先更新MySQL,再更新Redis”合理一些

  • 原因在于更新完Redis的话,哪怕还没更新MySQL,这时如果请求过来,读到的都是Redis更新后的新数据

  • 另外,先更新Redis成功,再更新MySQL失败,可以通过再删除Redis所对应的数据进行补救

  • 缺点:读到的都是Redis未生效的新数据

# 3、先更新MySQL,再删除Redis

  • 如果先更新MySQL成功了,还未对Redis进行删除的间隙期,这时如果请求过来,读到的都是Redis的删除前数据。

  • 如果先更新MySQL成功了,再删除Redis失败了的话,后面的请求读到的都是Redis的删除前数据,并且后续的补救方案很难做

# 4、先删除Redis,再更新MySQL

  • 如果先删除Redis成功了,还未对MySQL进行更新的间隙期

  • 只存在于MySQL一个存储载体中,也就没有了数据一致性的问题

  • 如果先删除Redis成功了,再更新MySQL失败了的话

  • 只存在于MySQL一个存储载体中,所谓的补救方案也就不需要了,直接当这条数据没更新成功

  • 产生问题

    • 某商品的库存数为10个,用户A购买一件商品时进行库存扣减,因此第一步先删除了Redis中的库存数
    • 这时,用户B查询该商品的库存,发现Redis中并没有该商品的库存,于是从MySQL中读取库存数后,写入到了Redis中(10个)
    • 然后,用户A更新数据库,将库存数从10个扣减为9个
    • 最终,Redis中的库存数是10个,MySQL中的库存数是9个

# 5、分布式锁

  • 分布式锁完全可以解决一致性问题,但是性能会降低,我们设置缓存的目的就是为了性能

# 02.ES和MySQL双写

  • 一致性要求适中

    • 推荐使用 消息队列解耦方案,既保证了高可用,又降低了系统耦合
  • 一致性要求高

    • 使用 分布式事务方案 确保强一致性
  • 一致性要求不高

    • 采用 异步校对修复机制 辅助即可

# 1、业务逻辑重试机制

  • 单独重试失败的一方

    • 当写入 MySQL 成功但写入 ES 失败时,将失败的操作记录在一个消息队列或本地事务日志中,并异步重试
    • 反之,当写入 ES 成功但写入 MySQL 失败时,同样记录并异步重试
  • 优点

    • 易于实现,逻辑清晰
  • 缺点

    • 如果频繁失败,会增加重试的系统开销

    • 在高并发场景下,可能造成数据延迟

# 2、基于消息队列的解耦

  • 采用消息队列实现写入的异步解耦

    • 业务逻辑优先写入 MySQL

    • 成功后,将数据变更事件(如新增、更新、删除操作)写入消息队列(如 Kafka、RabbitMQ)

    • 监听消息队列的消费者负责将变更写入 ES,确保最终一致性

  • 优点

    • 提高系统解耦性

    • 消息队列自带重试和持久化机制,减少失败影响

  • 缺点

    • 引入消息队列增加了系统复杂性

    • 需要处理消息重复消费的问题

# 3、双写事务保证(TCC)

  • MySQL 新插入的记录在全局事务完成前,状态字段设为 pending

    • # 通过业务唯一标识(如订单号 `id`)约束插入,避免重复写入
      INSERT INTO orders (id, status, data) VALUES (123, 'pending', 'order data');
      
      # 通过业务唯一标识(如订单号 id)约束插入,避免重复写入
      UPDATE orders SET status = 'completed' WHERE id = 123;
      
      # 如果 ES 写入失败,执行回滚(可物理删除或将状态更新为 failed)
      UPDATE orders SET status = 'failed' WHERE id = 123;
      
      1
      2
      3
      4
      5
      6
      7
      8
  • 插入一条带标记的文档,标记字段如 status: "unconfirmed"

    • 如果 MySQL 和 ES 的事务成功,更新状态为 confirmed

    • 如果事务失败,删除文档或将状态标记为 failed

    • PUT /orders/_doc/123
      {"id":123,"data":"order data","status":"unconfirmed"}
      
      1
      2

# 4、异步校对和修复机制

  • 通过异步校验定期检查 MySQL 和 ES 的数据一致性,并修复不一致的数据

    • 定期将 MySQL 数据和 ES 数据进行校验(如对比主键、版本号、哈希值等)

    • 找出不一致的数据并重新写入 ES 或 MySQL,确保最终一致性

# 5、基于状态标识的方案

  • 在 MySQL 中为每条数据维护一个状态标识,标记数据是否成功同步到 ES

  • 在 MySQL 表中新增一个字段

    • 0 表示未同步
    • 1 表示同步成功
  • 在业务写入 MySQL 时,默认设置为 0,然后通过异步任务同步到 ES,并更新状态为 1

  • 定期扫描 sync_status=0 的数据,重试同步

11.MySQL主从配置
13.MySQL查询缓存

← 11.MySQL主从配置 13.MySQL查询缓存→

最近更新
01
06.Mage平台
05-30
02
16.区块链交易所
05-28
03
01.常识梳理
05-28
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式