今日主题:高并发系统设计思路、核心问题落地方案、分库分表完整设计、经典场景实操细节(、
核心目标:吃透每个高并发问题的「底层原理+落地步骤+避坑点」,重点突破分库分表、缓存一致性等难点
一、高并发系统设计目标(精准定义+量化指标)
高并发不是“能扛住流量”,而是「在高流量下,兼顾高可用、高性能、可扩展」,
-
高并发:支撑大流量、高QPS/TPS,核心指标:QPS(每秒查询请求数)、TPS(每秒事务数)、并发数(同时在线请求数)
- 中小系统:QPS 1k-10k
- 中大型系统:QPS 10k-100k
- 大厂核心系统(如电商下单):QPS 100k+
-
高可用:服务不宕机、少报错、可容错,核心指标:可用性(99.9%=每年 downtime 8.76小时;99.99%=每年 downtime 52.56分钟)、MTBF(平均无故障时间,越长越好)、MTTR(平均恢复时间,越短越好)
- 落地要求:无单点故障、故障自动切换、快速恢复
-
高性能:低延迟、快响应,核心指标:平均响应时间(<200ms)、P99延迟(99%的请求响应时间<500ms)、P95延迟(95%的请求响应时间<300ms)
- 关键:减少IO等待(数据库、网络)、提升CPU利用率、避免无效计算
-
可扩展:加机器就能扩容,不依赖单点,核心是「无状态设计+水平扩容」,避免垂直扩容(单机升级硬件,有上限)
二、高并发三大核心思路(带落地细节)
1. 分流:把流量打散,避免单点承压(落地步骤+工具)
核心逻辑:将集中的流量,通过“分层分流”,分散到多个节点,避免单个服务/数据库被打崩,分3层实现:
-
第一层:DNS分流
- 原理:基于用户IP地理位置,将请求路由到就近的机房(如北方用户路由到北京机房,南方用户路由到上海机房)
- 工具:阿里云DNS、Cloudflare DNS
- 避坑:DNS缓存导致流量切换延迟,需设置合理的TTL(如10分钟)
-
第二层:网关/负载均衡分流
-
负载均衡策略:
- 轮询:简单,适合所有服务实例性能一致的场景(如无状态服务)
- 加权轮询:按实例性能分配流量(性能好的实例权重高),适合实例性能不均的场景
- 一致性哈希:解决“缓存雪崩”问题,适合缓存集群(如Redis分片),节点扩容时,仅影响少量数据
- 最小连接数:优先路由到连接数少的实例,适合长连接场景(如WebSocket)
-
工具:Nginx(网关层负载均衡)、HAProxy(高性能负载均衡)、服务内负载均衡(Dubbo/Feign内置)
-
-
第三层:服务/数据分片分流
- 服务分片:按业务域拆分(如用户服务、商品服务、订单服务),避免单服务承载所有业务
- 数据分片:分库分表,将单表数据分散到多表/多库,避免单库/单表瓶颈(详细设计见第四部分)
2. 异步:解放主线程,提升响应速度(落地方案+场景)
核心逻辑:将“非核心、耗时”的操作,从主线程中剥离,异步执行,主线程快速返回结果,提升用户体验,分2种落地方式:
-
方式1:CompletableFuture(服务内异步)
-
适用场景:服务内多个独立的耗时操作(如查询用户信息+查询商品信息+查询订单信息),可并行执行
-
落地细节:
- 创建线程池:单独创建异步线程池,避免使用默认线程池(核心线程数=CPU核心数+1,最大线程数=2*CPU核心数+1)
- 异常处理:必须处理异步任务的异常(exceptionally方法),避免线程泄露
- 结果聚合:使用thenCombine、thenAccept等方法,聚合多个异步任务的结果
-
-
方式2:消息队列(跨服务异步)
-
适用场景:非核心流程(如日志记录、短信通知、数据统计、订单异步落库)
-
落地细节:
- 队列选择:业务场景用RocketMQ,日志/大数据场景用Kafka
- 消息可靠性:开启持久化、手动ACK、事务消息(确保消息不丢失)
- 避免消息堆积:合理设置消费者数量,优化消费逻辑
-
3. 缓存:用内存扛读流量,减少数据库压力(多级缓存+落地细节)
核心逻辑:将热点数据缓存到“离用户最近”的地方,减少数据库IO,提升响应速度,必须用「多级缓存」,单一缓存有风险:
-
多级缓存架构(从用户到数据库,按优先级排序) :
-
浏览器缓存:Cache-Control(缓存时间)、ETag(资源校验),缓存静态资源(JS/CSS/图片),用户本地缓存,无需请求服务器
-
CDN缓存:就近节点缓存静态资源,减少源站请求,如阿里云CDN、腾讯云CDN,缓存命中率目标≥95%
-
本地缓存(JVM缓存):Caffeine(推荐)、Guava Cache,缓存服务内高频访问的热点数据(如商品基础信息),响应时间≤1ms
- 配置细节:初始容量=热点数据量1.5,最大容量=热点数据量2,过期时间=5-10分钟,避免内存溢出
-
分布式缓存(Redis):缓存全量热点数据(如商品详情、用户信息),支撑高并发读,响应时间≤10ms
- 架构选择:主从+哨兵(中小系统)、Redis Cluster(中大型系统,分片存储)
-
-
缓存命中率优化(核心指标,目标≥90%) :
- 缓存预热:系统启动时,批量加载热点数据到缓存(如商品详情、首页数据),避免冷启动时缓存未命中
- 缓存更新:数据库更新后,及时更新/删除缓存(见第四部分缓存一致性)
- 避免缓存穿透/击穿/雪崩:见第四部分详细方案
三、高并发架构分层核心知识点(带落地工具+配置细节)
1. 前端层(抗流第一道,落地可直接用)
-
静态资源优化(核心落地) :
- 资源处理:JS/CSS合并压缩(用Webpack)、图片压缩(用TinyPNG)、图片格式优化(WebP格式,比JPG小30%)
- CDN部署:静态资源(图片、JS、CSS、视频)全部部署到CDN,配置缓存规则(静态资源缓存1-7天)
- 懒加载:图片、视频懒加载(用IntersectionObserver),首屏只加载可视区域资源,减少首屏加载时间
-
请求拦截(减少无效请求) :
-
防抖:按钮点击防抖(延迟500ms执行,避免快速点击重复请求)
-
防重复提交:
- 前端:按钮置灰(点击后禁用,请求完成后启用)
- 后端:防重Token(请求前获取Token,执行后销毁,Redis存储Token,过期时间5分钟)
-
请求裁剪:只传递必要参数,避免传递大字段(如不传递完整用户信息,只传递用户ID)
-
2. 网关层(统一入口,流量控制核心)
主流工具:Spring Cloud Gateway(微服务首选)、Nginx(独立网关),核心功能落地:
-
路由转发:按URL路径路由到对应服务(如/api/user/*路由到用户服务,/api/order/*路由到订单服务)
-
灰度发布:按比例切流量(如10%流量路由到新版本服务,90%流量路由到旧版本),验证新版本稳定性
- 落地:Gateway通过Predicate匹配用户IP/用户ID,路由到不同版本的服务集群
-
限流、鉴权、黑名单:
- 限流:按IP(单IP每秒最多100请求)、用户ID(单用户每秒最多10请求)限流,超出阈值返回429状态码
- 鉴权:统一校验Token(JWT),无效Token直接拦截,不转发到后端服务
- 黑名单:维护恶意IP/用户黑名单,直接拦截请求(Redis存储黑名单,定期更新)
-
请求聚合:将多个小请求合并为一个请求(如查询用户信息+商品信息,网关一次性请求两个服务,聚合结果后返回),减少网络开销
3. 应用层(业务核心,无状态+隔离)
-
无状态设计(必做,否则无法水平扩容) :
- 禁止存储本地状态:不存Session、本地缓存(除Caffeine热点缓存)、线程本地变量(ThreadLocal)
- 状态统一存储:Session存储到Redis(Spring Session),业务状态存储到数据库/Redis
- 落地验证:服务实例可随意增减,不影响用户请求(如新增10个实例,用户请求路由到新实例,无异常)
-
隔离机制(避免级联故障) :
-
线程池隔离:不同业务用不同线程池(如用户服务线程池、订单服务线程池),避免一个业务线程池满,拖垮整个服务
- 配置:核心线程数=CPU核心数,最大线程数=2*CPU核心数,队列容量=1000,拒绝策略=降级(返回兜底数据)
-
服务隔离:通过集群分组隔离(如订单服务分为核心集群、非核心集群),非核心集群故障不影响核心集群
-
4. 服务层(微服务,治理+容错)
-
服务拆分(落地原则,避免过度拆分) :
- 拆分原则:单一职责、高内聚低耦合、按业务域拆分(如用户域、商品域、订单域、支付域)
- 避免陷阱:不按技术分层拆分(如Controller层、Service层拆分),不拆分过细(如一个接口一个服务)
- 拆分后通信:Dubbo(高性能,RPC协议)、OpenFeign(简单易用,HTTP协议),根据性能需求选择
-
服务容错(Sentinel落地细节) :
- 超时控制:调用下游服务时,设置超时时间(如1000ms),避免无限等待
- 重试机制:重试次数=1-2次,重试间隔=500ms,只对幂等接口重试(如查询接口),非幂等接口(如下单)不重试
- 熔断降级:配置熔断阈值(失败率≥50%,持续10秒,触发熔断),熔断后返回兜底数据(如“服务繁忙,请稍后再试”)
5. 数据层(扛压关键,缓存+数据库优化+分库分表)
-
数据库优化(基础,必须落地) :
-
索引优化:
- 必建索引:主键索引(ID)、业务唯一索引(如订单号、用户ID)、查询条件索引(如订单创建时间、商品ID)
- 避免陷阱:不建冗余索引(如建了联合索引(a,b),就不用建a索引)、不建全列索引、避免索引失效(如where条件用函数、模糊查询%开头)
-
慢SQL优化:
- 监控:通过MySQL慢查询日志(long_query_time=1秒)、Prometheus监控慢SQL
- 优化方向:避免全表扫描、避免大事务(事务执行时间<500ms)、避免批量更新/删除(分批次执行)
-
读写分离:主库写、从库读,分担读压力(MySQL主从复制,异步复制,延迟控制在100ms内)
- 落地:ShardingSphere、MyCat实现读写分离,读请求路由到从库,写请求路由到主库
- 避坑:读从库可能存在数据延迟,核心读请求(如订单详情)可路由到主库
-
-
分库分表(核心难点,详细设计见第四部分) :解决单库/单表容量瓶颈(单表数据量≥1000万时,必须分表)
四、高并发必解决的五大核心问题(超详细落地方案,含分库分表)
1. 分库分表完整设计方案
核心前提:单库数据量≥5000万、单表数据量≥1000万时,必须分库分表;优先水平分表,垂直分表仅作为补充,核心工具:ShardingSphere(国内主流,开箱即用)
(1)分库分表的三种方式(场景+选择)
-
方式1:垂直分表(列拆分)
-
定义:将一张表的“高频字段”和“低频字段”拆分到两张表,减少单表字段数,提升查询效率
-
适用场景:表字段过多(≥30个),且部分字段高频查询(如商品表的id、名称、价格),部分字段低频查询(如商品详情、备注)
-
实例(商品表拆分):
- 商品核心表(product_core):id(主键)、name(商品名称)、price(价格)、category_id(分类ID)、status(状态)(高频查询)
- 商品详情表(product_detail):id(主键)、product_id(关联商品核心表)、detail(商品详情)、remark(备注)(低频查询)
-
落地细节:查询时,通过product_id关联两张表,高频查询只查核心表,减少IO
-
-
方式2:垂直分库(库拆分)
-
定义:将不同业务域的表,拆分到不同的数据库(如用户库、商品库、订单库),避免单库压力过大
-
适用场景:业务拆分清晰,不同业务域的表关联性低(如用户表和订单表,仅通过用户ID关联)
-
实例:
- 用户库(user_db):user(用户表)、user_address(用户地址表)
- 商品库(product_db):product_core(商品核心表)、product_detail(商品详情表)、category(分类表)
- 订单库(order_db):order(订单表)、order_item(订单项表)
-
落地细节:跨库查询通过Feign调用对应服务,避免直接跨库联表查询(性能差)
-
-
方式3:水平分表(行拆分,最常用)
- 定义:将一张表的“行数据”,按指定规则拆分到多张表(同库或跨库),每张表的结构相同,数据不同
- 适用场景:单表数据量过大(≥1000万),且查询条件包含分片键(如订单表按用户ID分片,查询时携带用户ID)
- 核心:选择合适的分片键和分片规则,避免数据倾斜
(2)水平分表的核心设计(分片键+分片规则+扩容方案)
① 分片键选择(核心,决定分片效果)
原则:高频查询字段、均匀分布、不易变更,优先选择以下字段:
- 优先选择:用户ID、订单ID(分布式ID)、商品ID(分布式ID)
- 避免选择:时间字段(如创建时间,可能导致数据倾斜,某段时间数据过多)、非高频查询字段(如备注)
- 实例:订单表(order),分片键选择user_id(用户ID),因为查询订单时,几乎都会携带用户ID
② 分片规则(落地常用,3种)
-
规则1:哈希分片(最常用,均匀分布)
- 原理:对分片键(如user_id)进行哈希运算,取模(%)分片数量,决定数据存储到哪张表
- 实例:订单表分8张表(order_0 ~ order_7),分片规则:user_id % 8 = 0 → 存储到order_0,以此类推
- 优点:数据分布均匀,查询效率高(根据user_id直接定位表)
- 缺点:扩容困难(如从8张表扩容到16张表,需要重新哈希,迁移所有数据)
- 优化:使用一致性哈希,扩容时仅迁移部分数据(适合Redis分片,分库分表也可使用)
-
规则2:范围分片(适合按时间/ID范围查询)
- 原理:按分片键的范围拆分,如按订单ID范围、时间范围
- 实例1(按ID范围):订单表分4张表,order_0(ID 1-1000万)、order_1(ID 1001万-2000万)、order_2(ID 2001万-3000万)、order_3(ID 3001万+)
- 实例2(按时间范围):订单表按月份拆分,order_202603(2026年3月订单)、order_202604(2026年4月订单)
- 优点:扩容简单(新增表即可,无需迁移旧数据),适合按范围查询(如查询3月份的订单)
- 缺点:可能出现数据倾斜(如某月份订单量过大)
-
规则3:复合分片(复杂场景)
- 原理:结合哈希分片和范围分片,如先按user_id哈希分片,再按创建时间范围分片
- 适用场景:订单表既需要按用户ID查询,又需要按时间范围查询
- 实例:订单表先按user_id % 8 分成8个组,每个组内再按月份拆分(如order_0_202603、order_0_202604)
③ 扩容方案(落地关键,避免停机)
核心原则:扩容时不影响线上服务,数据迁移无感知,推荐两种方案:
-
方案1:范围分片扩容(最简单,推荐)
-
扩容步骤:
- 新增表(如订单表从4张扩容到8张,新增order_4 ~ order_7,对应ID范围3001万-8000万)
- 新数据自动写入新增表,旧数据无需迁移
- 查询时,ShardingSphere自动路由到对应表,无感知
-
适用场景:使用范围分片的表
-
-
方案2:哈希分片扩容(一致性哈希,无感知)
-
扩容步骤:
- 新增表(如从8张扩容到16张),配置一致性哈希规则
- 使用ShardingSphere的DataMigration工具,异步迁移数据(只迁移需要变更分片的部分数据)
- 迁移期间,读写请求正常路由(旧表和新表同时可用)
- 迁移完成后,删除旧表数据,扩容完成
-
适用场景:使用哈希分片的表
-
④ 分库分表避坑点
- 避免跨表/跨库联表查询:如订单表分表后,不要直接联表查询所有订单表,需通过服务聚合数据
- 避免数据倾斜:分片键选择要均匀,避免某张表数据量过大(如按用户ID哈希,避免某类用户ID集中)
- 避免分页查询问题:跨表分页(如查询所有用户的前100条订单),需通过ShardingSphere的分页优化,避免全表扫描
- 分布式事务:分库分表后,跨库事务需用TCC、事务消息保证最终一致性
- 索引设计:每张分表都要建相同的索引(如主键索引、分片键索引),避免索引缺失
(3)ShardingSphere落地配置
以订单表(order)水平分表(8张表)为例,使用哈希分片(user_id % 8):
-
- 配置分片规则:指定分片键为user_id,分片算法为哈希取模
-
- 配置分表节点:order_0 ~ order_7
-
- 配置读写分离:主库写,从库读
-
- 配置分布式ID:使用雪花算法生成订单ID(避免ID重复)
2. 缓存问题(四大问题,落地步骤+避坑)
(1)缓存穿透(查询不存在的数据,打崩数据库)
-
场景:黑客攻击(批量查询不存在的用户ID)、业务查询(如查询已删除的商品),缓存和数据库都查不到,请求直接打数据库
-
危害:数据库压力骤增,甚至宕机
-
落地解决方案(双重防护,必做):
-
缓存空值:将不存在的key缓存为null,设置短过期时间(如5分钟),避免重复查询数据库
- 避坑:缓存空值会占用Redis内存,需设置合理的过期时间,同时定期清理无效空值
-
布隆过滤器:在Redis之前拦截,判断key是否存在,不存在直接返回,不查缓存和数据库
- 落地:使用Redisson的布隆过滤器,初始化时加载所有存在的key(如商品ID、用户ID),查询前先判断
- 避坑:布隆过滤器有误判率(可设置,如0.01),误判时会查询缓存和数据库,不影响业务
-
(2)缓存击穿(热点key失效,大量请求打数据库)
-
场景:热点key(如爆款商品ID)过期,大量请求同时查询该key,缓存未命中,全部打数据库
-
危害:数据库瞬间压力过大,响应变慢,甚至宕机
-
落地解决方案(按优先级排序):
-
互斥锁(推荐,无侵入):使用Redis锁(Redisson的RLock),只允许一个线程重建缓存,其他线程等待
- 流程:请求查询缓存→未命中→获取锁→查询数据库→更新缓存→释放锁;其他线程获取锁失败,等待100ms后重试
- 避坑:锁的过期时间要大于缓存重建时间(如3秒),避免锁过期导致多个线程重建缓存
-
热点key永不过期:热点key不设置过期时间,后台异步更新缓存(如每5分钟更新一次)
- 避坑:需监控热点key的变化,及时更新,避免缓存数据过期
-
热点key预加载:系统启动时,将热点key加载到缓存,同时设置定时任务,在key过期前主动更新
-
(3)缓存雪崩(大量key同时过期/缓存宕机,请求全打数据库)
-
场景1:大量key设置了相同的过期时间(如凌晨2点),同时过期,请求全部打数据库
-
场景2:Redis集群宕机,缓存不可用,请求全部打数据库
-
危害:数据库崩溃,系统不可用
-
落地解决方案(分场景防护):
-
针对“大量key同时过期”:
- 过期时间加随机值:如原本过期时间1小时,改为1小时±10分钟,避免同时过期
- 分批设置过期时间:将热点key的过期时间分批设置(如一批1小时,一批1.5小时)
-
针对“缓存宕机”:
- 多级缓存:本地缓存(Caffeine)+ 分布式缓存(Redis),Redis宕机时,本地缓存兜底
- 缓存熔断:Redis宕机时,通过Sentinel熔断缓存查询,直接返回兜底数据(如“服务繁忙,请稍后再试”)
- Redis高可用:主从+哨兵(中小系统)、Redis Cluster(中大型系统),自动故障切换,避免单点故障
-
(4)缓存一致性(数据库更新,缓存未更新,数据不一致)
-
核心原则:读多写少场景,追求“最终一致性”;写多读少场景,追求“强一致性”(需牺牲性能)
-
落地解决方案(按场景选择):
-
方案1:先更数据库,再删缓存(推荐,读多写少场景)
-
流程:更新数据库→删除缓存→返回结果
-
优点:实现简单,最终一致,性能好
-
避坑:删除缓存失败,导致缓存数据过期,解决方案:
- 重试机制:删除缓存失败,加入重试队列(如RocketMQ),重试3次
- 定期同步:定时任务(如每10分钟)对比数据库和缓存数据,不一致则更新缓存
-
-
方案2:先删缓存,再更数据库(不推荐,有脏读风险)
- 风险:删除缓存后,更新数据库前,有请求查询缓存,缓存未命中,查询旧数据,写入缓存,导致数据不一致
- 优化:延迟双删(删除缓存→更新数据库→延迟1秒,再次删除缓存),减少脏读概率
-
方案3:Canal监听binlog(强一致性,写多读少场景)
- 流程:更新数据库→binlog日志→Canal监听→同步更新/删除缓存
- 优点:数据一致性高,无侵入(无需修改业务代码)
- 缺点:增加系统复杂度,需部署Canal服务
-
3. 接口限流(落地细节+工具配置)
-
限流核心配置(按场景设置) :
- 限流粒度:IP(单IP每秒最多100请求)、用户ID(单用户每秒最多10请求)、接口(单接口每秒最多1000请求)
- 限流阈值:根据服务性能设置(如单接口QPS上限=服务实例数*单实例处理能力)
- 限流返回:超出阈值返回429状态码+提示语(“请求过于频繁,请稍后再试”)
-
主流算法落地(工具+配置) :
-
令牌桶算法(推荐,支持突发流量):
- 工具:Sentinel、Nginx
- Sentinel配置:设置令牌生成速率(如1000个/秒),令牌桶容量(如2000个),支持突发流量(最多缓存2000个令牌)
-
滑动窗口算法(精度高,解决临界值问题):
- 工具:Sentinel、自定义实现
- 配置:窗口大小=1秒,拆分10个小窗口(每个100ms),滑动统计请求数,避免固定窗口的临界值问题
-
-
分层限流(落地必做) :
- 前端限流:按钮防抖、验证码,拦截无效请求
- 网关限流:Nginx/Gateway,拦截异常流量,避免打到后端服务
- 服务内限流:Sentinel,按接口限流,保护服务不被过载
4. 服务熔断 & 降级(Sentinel落地细节)
(1)熔断(下游服务故障,隔离故障)
-
Sentinel熔断配置(必背):
-
熔断阈值:失败率≥50%,持续时间≥10秒,最小请求数≥100(避免少量请求触发熔断)
-
熔断状态切换:
- 闭合状态:正常调用,统计失败率
- 打开状态:熔断开启,持续时间5秒,期间请求直接返回兜底数据
- 半开状态:熔断开启5秒后,允许10个请求尝试调用,失败率≥50%则重新打开,否则关闭熔断
-
兜底数据:返回默认值(如空列表、“服务繁忙”),不影响核心流程
-
(2)降级(自身过载,保核心功能)
-
降级触发条件(按优先级):
- 系统指标:CPU使用率≥80%、内存使用率≥90%、响应时间≥500ms
- 流量指标:QPS超出阈值、线程池满
-
降级策略(落地可直接用):
- 核心功能:不降级(如下单、支付)
- 非核心功能:降级(如评价、收藏、统计),返回兜底数据或关闭功能
(3)熔断与降级的核心区别
- 触发原因:熔断是「下游服务故障」,降级是「自身服务过载」
- 目的:熔断是「隔离故障,防止雪崩」,降级是「保核心,弃次要」
- 范围:熔断是「针对下游服务」,降级是「针对自身服务的非核心功能」
5. 幂等性(高并发必做,落地方案+场景)
核心:重复请求(网络重试、消费者异常、ACK失败)执行一次和执行多次结果一致,避免重复扣款、重复下单、重复入库
-
落地方案(按场景选择) :
-
方案1:唯一ID + Redis/数据库去重表(通用方案)
-
场景:下单、支付、短信发送
-
流程:
- 请求发起时,生成唯一ID(如UUID、雪花ID)
- 执行业务前,判断Redis/去重表中是否存在该ID
- 存在:直接返回成功(重复请求)
- 不存在:执行业务,执行完成后,将ID存入Redis/去重表(过期时间=业务最大处理时间+10分钟)
-
-
方案2:数据库唯一索引(避免重复插入)
- 场景:用户注册(手机号唯一)、订单创建(订单号唯一)
- 落地:给业务唯一字段建唯一索引(如手机号、订单号),重复插入时,数据库抛出唯一键冲突异常,捕获异常返回成功
-
方案3:状态机判断(订单/支付场景)
- 场景:订单状态流转(待支付→已支付→已发货→已完成)
- 落地:执行操作前,判断当前状态是否允许执行(如待支付订单才能支付,已支付订单无法重复支付)
-
方案4:防重Token(前端防重复提交)
-
场景:表单提交、按钮点击
-
流程:
- 前端请求获取防重Token(Redis存储,过期时间5分钟)
- 提交请求时,携带Token
- 后端验证Token,存在则执行业务,执行完成后删除Token;不存在则返回重复提交
-
-
五、高并发经典场景
1. 秒杀/抢券场景(高并发核心场景,必背)
核心需求:支撑高QPS(10万+)、防超卖、防重复下单、快速响应,落地流程(从前端到数据层):
-
前端层:
- 按钮防抖、置灰:点击后禁用,避免快速重复点击
- 验证码:秒杀前需输入验证码,拦截机器人请求
- 本地缓存:缓存商品库存(定时更新),减少请求次数
-
网关层:
- 限流:按IP(单IP每秒最多5请求)、用户ID(单用户每秒最多1请求)限流
- 鉴权:校验用户登录状态、秒杀资格(如会员才能参与)
- 请求过滤:拦截非法请求(如参数异常、恶意请求)
-
应用层:
-
库存预扣减:Redis Lua脚本原子操作(减少库存+判断库存是否充足),避免超卖
- Lua脚本逻辑:判断库存>0 → 库存-1 → 返回成功;否则返回失败
-
异步下单:预扣减成功后,将下单请求丢到RocketMQ,异步落库(避免主线程阻塞)
-
幂等性:用订单号(雪花ID)作为唯一标识,Redis去重,避免重复下单
-