【剖析高并发秒杀】从流量削峰到数据一致性的架构演进与实践

一、 挑战:三高背景下的数据库瓶颈

秒杀场景的核心挑战可以归结为“三高”:高并发、高性能、高可用。

而系统中最脆弱的一环,往往是我们的关系型数据库(如MySQL)。它承载着最终的数据落地,其连接数、IOPS和CPU资源都极其有限。如果任由海啸般的瞬时流量直接冲击数据库,结果必然是连接池耗尽、服务宕机,最终导致整个业务雪崩。

因此,我们的首要任务是设计一道坚固的防线,保护脆弱的数据库。

二、 架构演进第一阶段:缓存前置 + 异步落库,为性能而生的核心架构

为了应对高并发,我们的核心思路是:将写操作前置到缓存,通过消息队列异步持久化,实现流量削峰填谷

1. 前置阵地:Redis + Lua,保证原子性预扣库存

我们选择将库存等热点数据预热到Redis中,利用其卓越的内存读写性能来承接第一波流量。

但简单的 GET -> 业务判断 -> SET 操作在并发环境下存在严重的线程安全问题,极易导致超卖。此时,Lua脚本成为我们的不二之选。

codeLua

-- seckill.lua: 原子性校验与预扣库存
local voucherId = ARGV[1]
local userId = ARGV[2]local stockKey = 'seckill:stock:' .. voucherId
local orderKey = 'seckill:order:' .. voucherId-- 1. 检查库存
if(tonumber(redis.call('get', stockKey)) <= 0) thenreturn 1 -- 库存不足
end-- 2. 检查用户是否已下单 (利用Set数据结构)
if(redis.call('sismember', orderKey, userId) == 1) thenreturn 2 -- 已购买过
end-- 3. 扣减库存 & 记录用户
redis.call('incrby', stockKey, -1) -- 使用 incrby -1 代替 decr,语义更明确
redis.call('sadd', orderKey, userId)
return 0 -- 成功

核心优势: Lua脚本能在Redis服务端以原子方式执行,确保了“检查库存”、“判断重复”和“扣减库存”这三个步骤不可分割,从根本上杜绝了并发场景下的超卖和重复下单问题。

2. 流量缓冲带:消息队列(MQ),实现极致的削峰填谷

当Lua脚本执行成功,代表用户已获得购买资格。但我们并不立即操作数据库,而是将包含userId和voucherId的订单信息封装成一条消息,发送到消息队列(如RocketMQ)。

随后,系统可以立刻向前端返回成功响应(例如:“抢购成功,订单正在处理中…”)。

核心优势:

  • 极致性能与用户体验: 用户请求在毫秒级内完成,无需等待缓慢的数据库I/O。

  • 系统解耦与流量整形: MQ作为缓冲带,将瞬时的流量洪峰,转化成后端消费者服务可以平稳处理的涓涓细流,保护了下游所有服务。

至此,我们构建了一套高性能、高可用的异步架构。但这套架构为了性能,牺牲了数据的强一致性,从而引出了新的、更深层次的挑战。

三、 架构演进第二阶段:直面灵魂拷问,多场景下的数据一致性

异步架构带来了两个核心的一致性问题:

  1. 内部一致性: 如何保证异步链路(MQ -> 数据库)的可靠执行?

  2. 外部一致性: 如果有其他业务路径直接修改了数据库,缓存如何同步感知?

1. 保障内部一致性:异步链路的可靠性

这是首先要解决的问题。如果用户在Redis抢到了资格,但因为消费者服务异常导致数据库订单创建失败,对用户来说是不可接受的。

我们的保障措施有:

  • MQ的确认与重试: 消费者成功处理完数据库操作后,才向MQ发送ACK确认。如果消费失败(如数据库瞬时抖动),MQ会根据策略进行重试。

  • 数据库层面的幂等性: 在订单表上建立 (user_id, voucher_id) 的联合唯一索引。这是防止因MQ重试导致用户重复创建订单的最后一道、也是最坚固的防线。

  • 死信队列(DLX): 对于多次重试依然失败的“毒消息”,将其投入死信队列,并触发告警,等待人工介入处理。

2. 致命裂痕:外部不一致性带来的脏缓存

解决了内部链路的可靠性,一个更隐蔽的问题浮现了。我们的系统并非只有“秒杀”这一条路径会修改库存。考虑以下场景:

  • 场景A:运营后台补货。 运营人员通过管理后台为商品增加了100件库存。这个操作通常是直接更新数据库

  • 场景B:用户取消订单。 用户支付超时或主动取消订单,系统需要回滚库存,这个操作也极有可能是先更新数据库

在这两种场景下,数据库成为了数据更新的第一源头,而我们部署在Redis中的库存缓存对此一无所知!

后果是灾难性的: 数据库库存已经补充,但Redis库存仍为0,导致用户无法下单;或者数据库库存已经回滚,但Redis没有,导致商品被超卖。此时,Redis沦为了脏缓存

3. 终极方案:基于Canal的Binlog订阅模型

为了根治此问题,我们需要一种机制,让缓存能够“感知”到数据库的所有变化,无论这个变化来自哪个业务源头。我们将缓存同步的逻辑与业务逻辑彻底解耦,引入了基于数据库变更日志的同步方案。

核心思想: 数据库是所有数据的最终权威,其Binlog记录了所有的数据变更。我们只需要订阅Binlog,就能精确地知道数据何时、发生了何种变化。

架构流程:

  1. 开启MySQL Binlog: 确保数据库记录所有数据变更。

  2. 部署Canal服务: Canal伪装成一个MySQL的Slave节点,实时订阅并拉取主库的Binlog。

  3. 解析与投递: Canal解析Binlog,将结构化的数据变更消息(如哪个表的哪一行被更新了)投递到指定的MQ Topic。

  4. 专职消费者: 一个独立的、专门负责缓存维护的消费者服务订阅此Topic。当收到消息后,它会精确地解析出需要操作的Key,并执行缓存的更新或删除操作。

这套方案的巨大优势:

  • 彻底解耦: 所有业务代码(秒杀、后台、订单服务)都不再需要关心任何缓存维护逻辑,职责单一。

  • 终极可靠: 缓存的同步操作不再依赖于业务线程的执行结果。只要数据库主库的事务提交成功(即Binlog生成),缓存的同步操作就“一定”会发生。

  • 解决多源写入问题: 从根本上解决了因多个不同业务入口修改数据库而导致的缓存与数据不一致问题。

四、 架构安全网:不可或缺的兜底策略

没有100%完美的架构,我们还需要一些“安全网”来应对未知的异常。

  • 数据库层面的幂等性: (user_id, voucher_id) 联合唯一索引,是防止重复下单的最终防线。

  • MQ消费失败处理: 配置死信队列(DLX),兜底处理异常消息。

  • 缓存最终的守护神:设置TTL(过期时间): 为所有业务缓存设置一个合理的过期时间。这是最终的兜底方案,确保即使出现极端情况下的脏数据,它也不会永久存在,保证了系统的最终自我修复能力。

五、 总结

高并发秒杀系统的架构设计,是一场在性能、可用性与一致性之间不断权衡与演进的旅程。

我们始于 Redis+MQ 的缓存前置与异步化 架构,解决了高性能与高可用的核心诉求。

随后深入到问题的本质,通过 MQ重试、数据库唯一索引 等手段保障了异步链路的内部一致性,再通过引入 Canal订阅Binlog 的模型,完美解决了因多业务入口导致的外部数据一致性这一灵魂难题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96753.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redisson最新版本(3.50.0左右)启动时提示Netty的某些类找不到

文章目录一、写在前面二、解决方案1、解决方案2、一劳永逸3、确定redisson依赖netty的版本一、写在前面 Redisson最新版本&#xff0c;大概3.47.0&#xff0c;在JDK8环境下&#xff08;实测JDK17也一样&#xff09;会提示Netty的某些类找不到&#xff1a; Exception in threa…

MTK Linux DRM分析(八)- KMS drm_crtc.c

一、简介 Linux DRM(Direct Rendering Manager)子系统是内核中管理图形硬件的核心组件,而 CRTC(CRT Controller)又是其中的关键之一。它起源于过去控制阴极射线管(CRT)显示器的控制器概念,如今在现代图形显示中依旧扮演着至关重要的角色。 可以把 CRTC 想象成图形显示…

vue+openlayers示例:适配arcgis矢量瓦片服务以及样式(附源码下载)

由于单位这边有个项目是基于openlayers地图引擎框架实现webgis地图可视化功能&#xff0c;但是要调用第三方的arcgis矢量瓦片服务以及适配样式&#xff0c;在这个背景下&#xff0c;基于openlayersvue实现适配arcgis矢量瓦片服务以及样式效果&#xff0c;适合学习openlayers与前…

mybatis xml中表名 字段报红解决

mybatis xml中表名 字段报红解决

谷歌浏览器重定向url,谷歌浏览器浏览网页修改url到本地

谷歌应用商店搜索插件requestly&#xff08;有个相似名称的插件&#xff0c;选择这个Requestly: Supercharge your Development & QA&#xff09; 安装后打开插件网址https://app.requestly.io/rules/my-rules 新建规则rules->my rules-> new rule -> redirect …

教育场景下禁用html5播放器拖动进度条的例子

禁用视频课程进度条的拖动功能&#xff0c;主要是为了强制学员按照课程设计的顺序观看内容&#xff0c;防止跳过关键知识点&#xff0c;从而保证学习效果和课程的完整性。 1.防止应试作弊&#xff1a; 在一些需要观看视频才能解锁下一章节或完成测试的场景中&#xff0c;禁用…

async实战

一、协程 协程是程序员人为创造 协程是一种用户态内的上下文切换技术。通过一个线程实现代码块相互切换执行。yield返回生成器 yield from 代表&#xff0c;跳到 func2协程函数 通过函数名()&#xff0c;是执行不了的。需要把函数加入到loop里面来&#xff0c;才可以被执行。 把…

个人搭建小网站教程(云服务器Ubuntu版本)

目录 1.配置云服务器&#xff08;略讲&#xff09; 2.vscode连接&#xff08;ssh连接&#xff09; 3.本地压缩项目包 4.传输项目 5.配置项目依赖 6.运行项目 1.启动 FastAPI 后端&#xff08;Python 部分&#xff09; 2.启动 Next.js 前端&#xff08;Node.js 部分&…

pion/webrtc v4.1.4 版本发布:关键特性与性能优化全面解析

引言 实时通信技术在现代互联网应用中扮演着越来越重要的角色&#xff0c;从视频会议到在线教育&#xff0c;从远程医疗到物联网设备交互&#xff0c;WebRTC技术已经成为实时音视频通信的事实标准。作为Go语言中最成熟且广泛使用的WebRTC实现&#xff0c;pion/webrtc项目持续推…

集成算法(聚类)

下面简单集成算法代码from sklearn.datasets import make_blobs from sklearn.cluster import KMeans import matplotlib.pyplot as plt# 创建数据集&#xff0c;生成 3 个中心的聚类数据&#xff0c;共 300 个样本&#xff0c;每个样本 2 个特征 X, _ make_blobs(n_samples30…

01 网络信息内容安全--绪论

1 课程内容 网络信息内容获取技术网络信息内容预处理技术网络信息内容过滤技术社会网络分析技术入侵检测技术异常流量检测技术对抗攻击技术 2 理论研讨 分为16个组 2.1 网络信息内容获取技术&#xff1a;第1组 【用DeepSeek网站爬虫&#xff0c;数据获取零成本&#xff01…

GPT-5:天变了吗?还是风停了?

2025年8月8日&#xff0c;OpenAI 发布了 GPT-5。这次更新被许多人寄予厚望&#xff0c;也引发了不少争议。对普通用户来说&#xff0c;这是一场“又快又会做事”的智能盛宴&#xff1b;而对资深开发者和 AI 研究者而言&#xff0c;GPT-5 可能更像是一次不够激进、略显保守的版本…

生信分析自学攻略 | R语言数据筛选和修改

在《生信小白自学攻略》系列的前几篇文章中&#xff0c;我们已经了解了 R 和 RStudio 的安装、RStudio 的深度探索&#xff0c;以及 R 语言的基本数据类型和数据结构。现在&#xff0c;是时候深入探讨如何运用 R 语言对数据进行精细化处理了。本篇推文将详细介绍如何在 R 中对数…

从零开始学习概念物理(第13版)(1)

前言&#xff1a;对我来说&#xff0c;最有用的就是物理了&#xff0c;尤其是电磁学。但是要学好它&#xff0c;我得夯实我的基础&#xff0c;前面更加基础的数学和物理都不能拉下。现在我问了Deepseek推荐的国外物理书&#xff0c;这本《概念物理》是最适合我&#xff0c;等入…

CSS变量

元素背景需要统一&#xff0c;一个个设置修改起来很麻烦&#xff0c;也没有全局变量&#xff1f; CSS中的变量_css变量-CSDN博客 -- 前缀定义变量&#xff0c;var(--) 使用变量&#xff0c;:root 表示根元素。 :root { --bg:#222; --fg:#bbb; } body { background:var(--bg)…

C++(Qt)软件调试---vscode配置clang-tidy静态分析(30)

C(Qt)软件调试—vscode配置clang-tidy静态分析&#xff08;30&#xff09; 文章目录C(Qt)软件调试---vscode配置clang-tidy静态分析&#xff08;30&#xff09;[toc]1 概述2 clang-tidy基本用法3 目前已有检查项4 vscode配置clang-tidy5 .clang-tidy配置文件6 参考地址更多精彩…

每天自动备份oracle

oracle数据库比其他数据库都贵&#xff0c;但是自带的管理工具却很差&#xff0c;真不知道咋想的。想完成每天自动备份&#xff0c;只能自己动手(window环境)&#xff1a;1、创建个备份目录&#xff0c;如D:\databack2、创建个脚本&#xff1a;backup.bat核心内容如下&#xff…

HBase Coprocessor:扩展HBase功能的利器

HBase Coprocessor&#xff1a;扩展HBase功能的利器 关键词&#xff1a;HBase, Coprocessor, 协处理器, RegionServer, 分布式计算, 扩展功能, 二级索引 摘要&#xff1a;HBase作为Hadoop生态中的分布式列存储数据库&#xff0c;以高可靠性、高吞吐量和强一致性著称&#xff0c…

【Java后端】Spring Boot 实现请求设备来源统计与UA解析全攻略

Spring Boot 实现请求设备来源统计与UA解析全攻略 在 Web 应用的实际场景中&#xff0c;我们经常需要知道 请求来自哪里 —— 是 Android 手机&#xff1f;还是 iOS&#xff1f;或者是 PC 浏览器&#xff1f; 这类信息往往可以通过 User-Agent (UA) 来统计&#xff0c;进而帮助…

技术框架搭建:支撑竞拍全流程

纯竞拍的技术框架是一个多层协同的系统&#xff0c;从用户交互到数据处理&#xff0c;每个环节都有专门的技术组件提供支持。​前端层是用户与竞拍系统交互的窗口&#xff0c;核心目标是提供流畅、实时、直观的操作体验。采用 React、Vue 等主流前端框架构建单页应用&#xff0…