一文彻底搞定Redis与MySQL的数据同步

立山  金牌会员 | 2024-10-23 11:49:47 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 910|帖子 910|积分 2730

Redis 和 MySQL 一致性问题是企业级应用中常见的挑战之一,特别是在高并发、高可用的场景下。由于 Redis 是内存型数据库,具备极高的读写速率,而 MySQL 作为长期化数据库,通常用于数据的可靠存储,如何包管两者数据的一致性需要具体业务场景的设计与优化。

下面我们将结合几个典范的业务场景,逐步分析如安在不同的场景下包管 Redis 和 MySQL 之间的数据一致性。
1. 缓存更新计谋:Cache Aside Pattern(旁路缓存模式)

场景:

在大部分业务体系中,Redis 作为缓存层用于提拔体系的读取性能,而 MySQL 作为长期化存储,用于包管数据的可靠性。最常见的场景是:

  • 体系先查询 Redis 缓存,假如缓存中没有数据,再从 MySQL 中查询并将数据写入 Redis 缓存。
  • 更新数据时,更新 MySQL 并删除 Redis 缓存,使缓存数据失效,包管下次读取时能拿到最新数据。
典范业务场景:


  • 商品详情页面:当用户哀求某个商品详情时,首先查询 Redis 缓存,假如缓存中没有,则查询 MySQL,将查询结果缓存到 Redis 中;假如商品信息发生变更时,更新 MySQL 并删除 Redis 中的缓存。
方案分析:


  • 读取路径:从 Redis 获取缓存,假如缓存命中则直接返回数据;假如缓存未命中,则查询 MySQL,将结果写入 Redis,并返回数据。
  • 写入路径:更新时先操作 MySQL,然后删除 Redis 缓存中的数据。下次读取时,由于缓存未命中,会重新从 MySQL 中获取最新数据。
如何保障一致性:


  • 缓存淘汰计谋:MySQL 数据更新后立即删除 Redis 缓存,确保下次读取时能获取到最新数据。即通过 "删除缓存" 的方式制止脏数据存在于缓存中。
  • 并发问题:当并发哀求较高时,可能会出现“缓存雪崩”或“缓存击穿”问题。例如:A 更新 MySQL 数据,B 在缓存失效的瞬间读取了旧数据,再次缓存到 Redis。为解决此问题,可以接纳 延迟双删计谋

    • 删除 Redis 缓存。
    • 更新 MySQL。
    • 适当延迟(如 500ms),再次删除 Redis 缓存,确保在并发情况下不存在缓存不一致问题。

  • 业务实例:
    1. // 更新商品详情的伪代码
    2. public void updateProduct(Product product) {
    3.     // 1. 更新数据库
    4.     updateProductInMySQL(product);
    5.     // 2. 删除缓存
    6.     deleteProductCache(product.getId());
    7.    
    8.     // 3. 延迟双删,解决并发下不一致问题
    9.     try {
    10.         Thread.sleep(500);  // 可以根据实际业务场景调整
    11.     } catch (InterruptedException e) {
    12.         // handle exception
    13.     }
    14.     deleteProductCache(product.getId());
    15. }
    复制代码
2. 先更新缓存再更新数据库

场景:

在某些实时性要求较高的场景中,可以考虑先更新 Redis 缓存,然后再异步更新 MySQL 数据库。
典范业务场景:


  • 秒杀体系:例如商品库存的扣减,用户购买商品时,首先更新 Redis 中的库存数量,包管极低延迟的实时性体验。然后将变更异步写入 MySQL,确保长期化存储的一致性。
方案分析:


  • 读取路径:读取 Redis 缓存的库存信息,能够提供快速的读取响应。
  • 写入路径:更新 Redis 中的库存数量后,使用消息队列或其他异步机制将更新同步到 MySQL。
如何保障一致性:


  • 数据最终一致性:Redis 作为前端实时数据的缓存,MySQL 作为后端数据的长期化存储,接纳异步更新计谋时,一致性无法包管是强一致性,但可以通过使用消息队列等本领来包管最终一致性。异步写入 MySQL 时,假如操作失败,可以通过重试机制或补偿机制恢复一致性。
  • 业务实例:
    1. // 扣减库存的伪代码
    2. public void reduceStock(Long productId, int amount) {
    3.     // 1. 先更新 Redis 中的库存
    4.     redisTemplate.decrement("stock:" + productId, amount);
    5.    
    6.     // 2. 通过消息队列异步更新 MySQL 中的库存
    7.     sendUpdateStockMessage(productId, amount);
    8. }
    9. // 消费消息队列更新 MySQL
    10. @RabbitListener(queues = "stock_update_queue")
    11. public void updateStockInMySQL(UpdateStockMessage msg) {
    12.     // 从 MySQL 中扣减库存
    13.     productRepository.reduceStock(msg.getProductId(), msg.getAmount());
    14. }
    复制代码
一致性包管计谋:


  • 幂等性保障:确保消息的处理是幂等的,即雷同的消息即使被处理多次,也不会导致库存重复扣减。
  • 消息重试机制:假如消费消息时更新 MySQL 失败,可以设置重试机制或消息补偿机制,包管最终数据一致性。
3. 双写操作(缓存与数据库同时更新)

场景:

有时业务需要同时更新 Redis 和 MySQL 的数据,如用户余额更新、积分奖励体系等场景中,Redis 和 MySQL 需要同步写入。
典范业务场景:


  • 积分体系:用户消费时增加或减少积分,需要同时更新 Redis 和 MySQL 中的积分记录。
方案分析:


  • 同步写入:当更新用户积分时,Redis 和 MySQL 同时更新数据。由于需要包管两个存储的同步性,必须考虑事务性问题。
  • 分布式事务:假如体系架构分布式,可能需要使用分布式事务(如 2PC,或者更轻量的解决方案如 TCC)来确保一致性。
如何保障一致性:


  • 双写一致性问题:假如同时写 Redis 和 MySQL,可能会面临一致性问题。常见解决方案是通过事务补偿机制来实现。具体步骤:

    • 使用数据库事务包管 MySQL 写入成功。
    • 假如 Redis 写入失败,可以实验重试,或在事务结束后通过补偿机制将失败的数据写入 Redis。

  • 业务实例:
    1. @Transactional
    2. public void updateUserPoints(Long userId, int points) {
    3.     // 1. 更新 MySQL 中的积分
    4.     userRepository.updatePoints(userId, points);
    5.    
    6.     // 2. 同步更新 Redis 中的积分
    7.     redisTemplate.opsForValue().set("user:points:" + userId, points);
    8. }
    复制代码
事务性保障:


  • 本地事务:在单体体系中,可以依赖数据库事务和 Redis 的操作包管一致性。假如操作失败,通过重试机制来恢复一致性。
  • 分布式事务:在微服务架构中,双写操作涉及分布式事务,可能需要使用 TCC(Try, Confirm, Cancel)等模式,或使用消息队列进行最终一致性补偿。
4. 数据回写(Write Back)计谋

场景:

数据回写模式适用于 Redis 作为缓存层,MySQL 作为长期化存储层,但 Redis 中数据修改后并不立即同步更新 MySQL,而是在特定时机触发数据回写。
典范业务场景:


  • 广告计费体系:广告点击量保存在 Redis 中,以减少频繁的数据库写入压力,定期将 Redis 中的统计数据批量写入 MySQL。
方案分析:


  • 延迟回写:可以通过定时使命或者触发器将 Redis 中的数据定期回写到 MySQL,这样既减少了 MySQL 的压力,又包管了数据一致性。
如何保障一致性:


  • 长期化与批量同步:通过 Redis 的长期化机制(如 RDB、AOF),在 Redis 瓦解时不会丢失数据。通过定时器或事件驱动体系触发批量同步 MySQL。
总结

Redis 和 MySQL 的一致性保障在不同的业务场景中需要结合场景特性来进行权衡,主要的计谋包括:

  • Cache Aside Pattern(旁路缓存模式):常用于读多写少的场景,写操作时删除缓存。
  • 异步更新(Write Behind):先更新缓存再异步写入 MySQL,包管最终一致性。
  • 双写计谋:同时更新 Redis 和 MySQL,共同事务机制确保一致性。
  • 延迟回写:通过定时批量写入 MySQL 减少频繁数据库操作。
每种计谋有不同的适用场景,设计时需要考虑一致性、性能和可用性之间的平衡。这算得上是全网最全最详细的,货真价实的同步方案分析了,完全结合真实业务场景来考虑设计。所谓赠人玫瑰,手留余香,希望对你有帮助作用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立山

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表