当前位置: 首页 > news >正文

深度解析:Redis 预扣减与 RabbitMQ 异步解耦,如何完美平衡延迟与一致性?

🚀 深度解析:Redis 预扣减与 RabbitMQ 异步解耦,如何完美平衡延迟与一致性?

💡核心导读
在高并发架构中,“延迟(Latency)”“一致性(Consistency)”天生就是一对死敌。
要想快(低延迟),就不能等数据库慢慢落盘;要想准(强一致性),就必须加锁排队,系统必然会慢。

Redis 预扣减 + RabbitMQ 异步解耦的核心思想,本质上是放弃了传统的“强一致性(ACID)”,转而全面拥抱分布式系统中的“BASE 理论”。通过引入“软状态(排队中)”,最终实现数据的“最终一致性(Eventual Consistency)”

下面为你深度拆解,这套架构是如何在两者之间走钢丝,做到既快又稳的:

⚡️ 一、 极致降低延迟:把耗时操作全部“踢”出主线程

为了做到极低的延迟,Controller 在处理用户的 HTTP 请求时,绝对不碰任何耗时的 I/O(如 MySQL 事务、外部 RPC 调用)。

主线程只做三件超快的事:

  1. 🧠查本地内存:耗时 < 0.1ms。
  2. 🏎️Redis Lua 脚本扣库存:单机 Redis QPS 可达 10万+,网络 + 执行耗时约 1~2ms。
  3. 📨把消息丢进 RabbitMQ:仅仅是网络投递,耗时约 2~5ms。

🎯极致结果:一个秒杀请求从发起到返回“抢购成功,正在排队”,后端总耗时在 10 毫秒级别。用户体验极其丝滑,系统吞吐量拉满。
⚠️遗留问题:这也挖了一个坑——Redis 里库存扣了,但 MySQL 里的库存还没扣,订单也没生成。此时数据是不一致的。

🎭 二、 用“软状态”掩盖延迟:产品与技术的完美配合

在数据到达最终一致之前,系统处于一个“中间状态”(软状态)。如果这个时候直接告诉用户“购买成功”,但最后 MySQL 扣款却失败了,用户一定会发飙。

如何平衡?答案是:靠前端的交互设计!

  • 💬话术转换:Controller 返回的不是“订单已生成”,而是“已抢到名额,正在排队处理”
  • 🏃视觉缓冲:前端展示一个友好的动画(比如一只奔跑的猫,或者加载进度条)。
  • 🔄后台轮询:前端在后台以 1次/秒 的频率,通过另一个接口去轮询(或建立 WebSocket),查询真实的订单状态。

偷来的时间:这短短的2~5 秒钟动画时间,就是技术团队从用户那里“偷来”的缓冲期。在这段时间里,后端的 RabbitMQ 和消费者(MySQL)正在疯狂补平数据。

🛡️ 三、 兜底最终一致性:RabbitMQ 的“死磕”机制

既然把压力交给了异步的 RabbitMQ 和消费者,就必须保证这条消息绝对不能丢,且不能被重复执行。这是平衡一致性的核心技术:

📦 1. 怎么防“丢”?(可靠性投递与消费)

如果在偷来的 5 秒钟里消息丢了,或者服务器宕机了,用户的名额就彻底成了黑洞。

  • ✅ 生产者确认(Publisher Confirms):Controller 发消息给 MQ 后,必须收到 MQ 的 ACK,才给用户返回“排队中”。如果没收到,就重试或回滚 Redis 库存。
  • 💾 MQ 持久化:开启交换机、队列和消息的持久化,就算 MQ 宕机重启,消息依然安全躺在磁盘上。
  • 🔒 消费者手动确认(Manual ACK):消费者(订单服务)从队列拿到消息后,先写 MySQL。只有 MySQL 的本地事务成功提交了,才向 MQ 发送 ACK。如果处理期间 Tomcat 挂了,MQ 会把消息重新放回队列,交给下一台机器处理。
🚫 2. 怎么防“重”?(消费端幂等性)

MQ 有个特点:为了保证绝对不丢,在网络抖动时可能会重复投递同一条消息。

  • 🆔 全局唯一 ID:每个丢进 MQ 的消息必须带上一个唯一的 RequestID 或流水号。
  • 🗄️ 数据库兜底:在 MySQL 订单表中,将 RequestID 或 (UserID + GoodsID) 设为唯一索引(Unique Key)。即使 MQ 重复发了两次同一条消息,MySQL 在执行第二次 Insert 时也会抛出 DuplicateKeyException(唯一键冲突)。代码捕获此异常并直接向 MQ 返回 ACK 即可,绝对不会超卖或生成双份订单。

🔄 四、 最后的闭环:超时未支付与对账(逆向回滚)

业务的一致性不仅在于正向的“创建”,更在于逆向的“回滚”。

🎬典型场景:用户抢到了(Redis 扣了,MQ 发了,MySQL 订单也生成了),但他后悔了,15分钟没付款。此时必须把库存加回去,否则就成了“少卖”。

🛠️解决方案:引入延迟队列(Delay Queue)

  1. 设定闹钟:订单生成时,顺便往 RabbitMQ 的“延迟队列”发一条消息,设置延迟时间为 15 分钟。
  2. 到点检查:15 分钟后,消费者收到这条消息,去 MySQL 查订单状态。
  3. 执行补偿:如果发现还是“待支付”,则触发完整的逆向回滚逻辑:
    • 关闭 MySQL 订单
    • 恢复 MySQL 库存(UPDATE goods SET stock = stock + 1)
      恢复 MySQL 库存(UPDATE goods SET stock = stock + 1)
    • 📈恢复 Redis 库存(执行 Lua 脚本 INCR 加 1)
    • 🧹清理本地售罄标记(通过 MQ 广播通知所有 Controller 节点清除 ConcurrentHashMap 里的售罄标记,让后续请求可以重新进来)

💡 总结

在这套高并发架构中,平衡延迟与一致性的哲学非常清晰:

  • 👤对用户(前端):只做最轻量级的检查(内存 + Redis),牺牲强一致性来换取极致的低延迟响应
  • 💻对系统(后端):利用软状态(排队动画)争取时间,通过 RabbitMQ 的可靠机制 + MySQL 唯一约束 + 延迟补偿,死磕到底,最终实现数据的绝对一致

🍔大白话比喻 —— 去网红餐厅吃饭(秒杀):
前台服务员(Controller + Redis)只负责秒速发给你一张“叫号凭证”(低延迟),而不是让你在门口傻等厨师现炒(阻塞);
而后厨(MQ + MySQL)拿着排队单子,按自己的最大产能有条不紊地颠勺炒菜;
只要这张排队单子没弄丢(可靠性投递),最后你一定能吃到饭(最终一致性)。

http://www.jsqmd.com/news/491891/

相关文章:

  • 科技中介机构如何优化技术交易流程?
  • 创建型,结构型,行为型设计模式全面比较
  • 双机热备/高可用架构中常见的一个网络问题
  • 春秋云境CVE-2019-13396
  • 堆内存和栈空间对任务创建的影响
  • 深入理解计算机系统:CPU 里面根本没有减法器?揭秘计算机的 0 和 1 是如何计算的
  • 2.Adobe Animate散件、绘制对象、组、元件
  • 2026年除甲醛品牌TOP10揭晓:谁才是真正靠谱的行业首选?
  • 排土场在线监测厂家核心竞争力对比:2026高性价比品牌推荐 - 深度智识库
  • Win10/11系统中检测电池健康的工具有哪些?
  • 【ABAP】ALV 指定单元格染色
  • LeetCode Hot 100——贪心部分
  • spaCy v2.0:自定义流水线组件与扩展属性实战
  • 赋能智慧电厂:一块开发板如何重塑能源安全巡检的底层逻辑
  • 相比高防IP,为什么现在的游戏公司更倾向于选择“湘情盾”?
  • 2026年全国精密传动设备供应商选型测评:行星减速机与中空旋转平台综合指南 - 深度智识库
  • 从标准件到定制化:2026车床刀座选型全流程指南与品牌推荐 - 品牌推荐大师1
  • Linux 基础IO (五)深入理解文件系统
  • 国产化编辑器如何扩展KindEditor的Excel公式导入?
  • 将文本转化为向量化表示
  • ansys apdl 车轨耦合车桥耦合 列车模型:考虑车体、转向架、车轮质量和二系悬挂 钢轨
  • 计算机毕业设计springboot高校学生党员信息管理系统 基于SpringBoot的高校党建信息化管理平台 基于SpringBoot的智慧校园党员服务系统
  • 全志H618
  • ceph提供rbd存储
  • 飞函私有化,安全重塑跨部门协作
  • 建议收藏|2026知网新规下如何降AI?国内外5款降低AIGC率工具实测(含免费降AI教程) - 殷念写论文
  • Unity Shader 实战:从零掌握 PBR 基于物理的渲染
  • django基于大数据技术的医疗数据分析与研究
  • CoPaw网页爬虫skill技能及定时任务管理
  • Linux 命令之 uname 详解(查看系统信息)