借助 Redis 锁,完美解决高并发秒杀问题:从原理到实战的 2 万字详解
发布时间:2026/10/1 12:43:58来源:尧图网络
一、写在前面一次秒杀事故带来的启示秒杀几乎是互联网应用中“高并发”三个字最直观的代名词。电商大促、限量优惠券、热门演出门票、稀缺商品补货这些业务都有一个共同特征商品数量极少关注人数极多活动开始的一瞬间大量请求像洪水一样涌向系统。一个看似简单的问题——“把库存减一库存为零就停止售卖”——在高并发下会迅速暴露出超卖、重复下单、缓存穿透、数据库扛不住、接口响应变慢等一系列连锁问题。可以回想一个非常典型的故障场景凌晨 0 点活动开始仅有的 100 件商品最终却产生了 180 笔有效订单。用户看到“抢购成功”随后又被通知“库存不足自动退款”大量客诉随之而来更严重的是数据库连接数被打满整个下单链路从秒杀接口开始拥堵最终拖垮了普通商品浏览和下单服务。表面上看是库存判断出了问题实际上暴露的是并发场景下资源互斥、原子操作、缓存与数据库一致性、接口幂等与限流等一整套架构能力缺失。本文将以“借助 Redis 锁完美解决高并发秒杀问题”为主题从并发困境拆解、Redis 分布式锁原理、安全解锁、锁续期、Redisson 实战再到库存原子扣减、缓存预热、防重复下单、防黄牛限流、整体代码实现与压测调优进行一次系统性的长文梳理。文章不是简单给出一个加锁示例而是希望帮助读者真正建立起一套可落地、可扩展、可运维的高并发秒杀方案并在理解原理的基础上避免各种容易踩到的坑。需要特别说明的是秒杀问题通常无法只靠“加一把锁”彻底解决。Redis 锁解决的是库存竞争、重复点击等并发正确性问题流量治理、削峰填谷、缓存预热、异步化扣减、最终一致性保障等内容同样重要。因此本文会在讲解 Redis 锁的同时把这些配套方案一并展开力求形成完整闭环。二、高并发秒杀场景为什么困难2.1 秒杀业务的核心特征理解秒杀难点首先要看清楚它与普通业务在流量模型上的差异。普通电商浏览、搜索、下单流量相对平缓读写比例较为均衡而秒杀活动具有非常强烈的“瞬时脉冲”特征。活动开始前用户不断刷新页面、请求倒计时和活动状态活动开始的一瞬间大量请求集中到达系统需要在极短时间内处理海量并发。从数据特征上看秒杀又是一个典型的“读多写少”场景。活动开始前商品详情、活动状态、剩余库存等读请求占绝对多数活动开始后虽然写请求明显增加但真正能够抢到商品的只是少数用户大多数请求最终会以“库存不足”或“未抢到”结束。这意味着如果所有请求都直接穿透到数据库数据库会在活动开始前就被读流量打满活动开始后又会被写竞争拖垮。2.2 三个必须解决的核心问题第一超卖问题。这是最致命的问题。库存只有 100 件但由于并发下库存查询、判断、扣减不是原子的可能出现多个线程同时读到库存为 1然后都执行“库存减一”最终导致 1 件商品卖出多单。超卖直接损害平台信誉并带来大量退款、投诉和财务对账成本。第二重复下单问题。用户在紧张情绪下会快速多次点击“立即抢购”网络抖动、前端按钮未及时置灰、接口重试等因素也会造成同一用户、同一商品被重复提交。即使库存扣减正确也会因为重复下单导致同一用户占用多份库存真实用户反而无法购买。第三系统稳定性和公平性问题。海量请求直达数据库要么连接池耗尽要么锁竞争极其激烈出现大量超时、雪崩同时如果不做排队和限流脚本和黄牛会占用大量请求资源普通用户很难公平地抢到商品。秒杀能否成功不只是并发正确性问题还是可用性、健壮性和公平性问题。2.3 一些看似可行、实际有隐患的方案单机应用中最常见的做法是使用 JVM 内的锁例如synchronized或ReentrantLock。在单个进程内这种方式确实可以保证多个线程对共享库存变量的互斥访问。然而秒杀服务通常部署在多实例集群中单机锁只能保护一个 JVM 内部的资源无法协调不同机器之间的并发操作。如果 A、B 两个服务实例各自扣减库存仍然会出现两个实例同时通过库存校验的情况超卖无法避免。数据库行锁或悲观锁也是一种选择。通过SELECT ... FOR UPDATE锁定库存行再执行扣减可以保证数据库层面的强一致。但数据库连接本身是昂贵资源秒杀请求海量到达时连接和行锁阻塞会非常严重吞吐量极低数据库 CPU、锁等待和连接数都会迅速飙升不适合作为核心拦截手段。数据库唯一索引可以用来防重复下单但通常只能作为最后一道防线不适合承接全部流量。因此我们需要一种高性能、跨进程、具备原子操作能力的分布式协调方案而 Redis 恰好能够同时满足“高性能”“跨实例”“命令原子性”三个关键要求成为高并发秒杀架构中的核心组件。三、Redis 在秒杀架构中的定位3.1 Redis 为什么适合承担秒杀核心组件Redis 是基于内存的高性能键值数据库单线程模型使得其基础命令天然具备原子性。以库存扣减为例如果我们预先将库存放到 Redis 中并通过 Lua 脚本或带条件的命令执行“查询库存、判断库存、扣减库存”这一系列操作整个过程不会被打断从而避免多线程同时读到旧库存导致的超卖。Redis 通常可以轻松支撑每秒数万甚至数十万级别的简单读写操作远超大多数关系型数据库。在秒杀场景中把热点库存、活动状态、用户抢购记录等高频访问数据放到 Redis 中可以显著降低数据库压力。此外Redis 提供了丰富的过期时间、发布订阅、Lua 脚本、分布式锁原语等能力非常适合构建高性能的并发控制机制。3.2 “Redis 缓存”和“Redis 锁”是两个维度很多开发者容易把 Redis 的两种用途混为一谈实际上它们是不同层面的能力。Redis 缓存解决的是“读多”问题目标是减少数据库访问、提高查询速度典型用法是把商品详情、活动配置缓存起来。Redis 锁解决的是“并发写”问题目标是在多个进程或服务实例之间建立互斥关系保证同一个商品、同一个用户在某一个时刻只能有一个请求进入关键业务逻辑。在秒杀系统中二者往往协同工作Redis 缓存负责保存库存信息和活动状态Redis 锁负责保护库存扣减、防重复下单等关键资源的并发安全。理解了这一点才能在设计方案时准确判断应该在哪些环节缓存、哪些环节加锁、哪些环节异步化。3.3 Redis 不是银弹虽然 Redis 性能高、使用方便但 Redis 本身也可能成为压力点并且 Redis 与数据库之间存在数据一致性、缓存击穿、缓存穿透、热点 Key 等问题。如果 Redis 宕机或网络抖动依赖 Redis 的秒杀链路也会受到影响。因此生产环境中需要部署高可用 Redis例如使用哨兵模式或 Redis Cluster同时设计降级方案比如 Redis 不可用时直接走数据库唯一约束防超卖以保证系统不会完全不可用。四、分布式锁基础从单机锁到分布式协调4.1 锁的本质是什么锁的核心目的是保证在同一时刻最多只有一个执行主体能够进入受保护的临界区。单机锁的对象是线程分布式锁的对象则是不同机器上的线程或进程。一个可靠的分布式锁至少应该满足三个条件第一互斥性同一时刻只能有一个客户端持有锁第二无死锁即使加锁方崩溃或忘记释放锁也能通过过期机制被自动释放第三安全性解锁时只能释放自己持有的锁不能误删其他客户端的锁。除此之外生产级分布式锁还应该考虑容错性。比如 Redis 主从切换过程中可能出现锁信息尚未同步到从节点主节点宕机后从节点提升为主节点导致另一个客户端拿到同一把锁。尽管这些属于比较极端的场景但在金融、交易等强一致要求较高的业务中需要认真评估必要时引入 RedLock 等更严格的方案。4.2 分布式锁需要避免的三个低级错误加锁和设置过期时间分开执行如果加锁成功后进程在设置过期时间之前崩溃锁会永远无法释放造成死锁。解锁时直接删除 Key如果持锁客户端因为业务执行过久导致锁自动过期另一个客户端已经获取了锁此时第一个客户端继续执行删除操作就可能误删第二个客户端的锁。忽略锁续期业务逻辑执行时间不确定时如果锁过期时间设置过短临界区还没执行完锁就失效了其他请求会进入造成并发安全问题。这些问题看起来简单但在高并发生产环境中非常容易引发难以排查的偶发故障。接下来我们会逐步展开 Redis 锁的正确实现方式把这些问题一一解决。五、Redis 锁的实现演进从 SETNX 到安全可用的分布式锁5.1 最原始的 SETNX 方案及其缺陷最早期的 Redis 分布式锁大多使用SETNX命令即SET if Not eXists。如果 Key 不存在则设置成功并返回 1如果 Key 已经存在则设置失败并返回 0。开发者通常会用这种方式实现加锁加锁成功后再为 Key 设置过期时间。这种写法有一个非常经典的缺陷加锁和设置过期时间不是原子操作。如果进程执行完setnx后、执行expire前宕机或被强制终止锁将永远存在其他所有请求都无法再次获得锁系统出现死锁。下面是这种有问题的实现示例public boolean tryLockWrong(Jedis jedis, String lockKey, String requestId, int expireSeconds) { Long result jedis.setnx(lockKey, requestId); if (result ! null result 1L) { // 如果这里进程崩溃锁将永远不会过期 jedis.expire(lockKey, expireSeconds); return true; } return false; }这段代码在低并发或测试环境中可能表现正常但在生产环境任何一个毫秒级的宕机或网络中断都可能造成致命的死锁。因此这种非原子性的加锁方式必须被替代。5.2 使用 SET NX EX 实现原子加锁为了解决上述问题Redis 提供了SET key value NX EX seconds形式的命令。它可以在一条命令中同时完成“仅当 Key 不存在时设置值”和“设置过期时间”两个动作从而保证加锁的原子性。在 Jedis 中可以通过SetParams来配置 NX 和 EX 参数。public boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireSeconds) { SetParams params new SetParams().nx().ex(expireSeconds); String result jedis.set(lockKey, requestId, params); return OK.equals(result); }这里必须单独强调requestId的作用。每个客户端在尝试加锁前都应该生成一个唯一标识通常可以使用 UUID 加线程名来保证不同客户端和不同线程之间的唯一性。这个唯一标识在后续解锁时会被用来校验锁的归属避免误删其他客户端的锁。5.3 只生成唯一标识还不够解锁必须校验所有权加锁时使用了唯一标识解锁时也必须带上这个标识。最常见的错误解锁方式是直接执行DEL lockKey。例如线程 A 获取锁后业务执行时间超出了锁的过期时间锁自动释放随后线程 B 成功获取锁并开始执行业务。此时线程 A 终于执行完业务逻辑并调用直接删除 Key 的方法这把线程 B 的锁删掉了。紧接着线程 C 又获得了锁多个线程并发进入临界区数据一致性被破坏。正确做法是解锁时先取出锁的值判断它是否是自己设置的那个唯一标识只有一致时才执行删除。判断和删除需要放在同一个原子操作中完成否则“判断后、删除前”仍然可能发生锁过期和所有权转移。因为 Redis 基础命令无法用一条普通命令完成“比较后再删除”所以需要借助 Lua 脚本。5.4 使用 Lua 脚本保证解锁原子性Lua 脚本在 Redis 中执行时会独占单线程脚本中的多个命令会作为一个整体执行不会被其他命令插入。这样我们就可以安全地先判断锁的值是否属于当前请求再决定是否删除。public boolean unlock(Jedis jedis, String lockKey, String requestId) { String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; Object result jedis.eval(script, 1, lockKey, requestId); return Long.valueOf(1L).equals(result); }上述脚本先从 Redis 中读取锁的值与传入的requestId比较如果相等才删除锁并返回 1否则返回 0。这样即使锁已经过期并被其他客户端重新持有原持有者也只会返回 0不会误删新锁。5.5 加锁流程完整梳理一个基础的 Redis 锁完整流程如下生成全局唯一的requestId。使用SET lockKey requestId NX EX seconds尝试获取锁。返回 OK 表示加锁成功进入业务逻辑否则根据业务策略直接失败、自旋重试或排队等待。业务执行完成后在finally块中通过 Lua 脚本校验并释放锁。如果业务执行时间可能超过锁的过期时间需要增加锁续期机制。六、锁的安全释放与过期时间设计6.1 过期时间应该设置多长锁过期时间没有万能值它取决于业务逻辑的耗时、下游接口的超时时间、网络抖动程度以及服务负载情况。如果设置太短业务尚未完成锁就失效其他请求进入后可能造成并发写冲突如果设置太长一旦客户端崩溃其他请求需要等待较长时间才能重新获取锁提升故障影响范围。建议先统计核心接口的 P99 耗时再结合网络和 GC 停顿预留安全余量。比如秒杀库存预扣接口在正常情况下 50 毫秒内完成P99 不超过 200 毫秒那么锁过期时间设置为 10 秒通常比较安全。但“通常”并不意味着完全可靠任何一次下游超时、数据库慢查询、Full GC 或网络重试都可能让业务耗时突破阈值。因此生产级方案应配套续期机制而不是盲目依赖一个固定过期时间。6.2 一定要在 finally 中释放锁无论业务成功、失败还是抛出异常都必须保证锁能够被释放。否则一次异常就会导致后续所有请求无法进入锁 Key 只能等待过期后恢复造成短时间内的业务不可用。下面的模板是典型的正确写法public void seckill(String userId, String goodsId) { String lockKey lock:seckill: goodsId; String requestId UUID.randomUUID().toString(); try { if (tryLock(jedis, lockKey, requestId, 10)) { // 执行秒杀核心逻辑 deductStock(userId, goodsId); } } finally { unlock(jedis, lockKey, requestId); } }需要注意unlock方法内部通过 Lua 脚本进行归属校验因此即使在极端情况下锁已经发生过期并易主也不会误删其他请求的锁。6.3 锁的可重入性问题在某些业务代码中一个线程可能在持有锁的情况下再次尝试获取同一把锁例如外层方法已经加锁内部调用的另一个方法也执行了加锁逻辑。如果锁不可重入第二次加锁会失败或被自己阻塞严重时导致死锁。是否支持可重入取决于具体框架实现和业务需要。对秒杀场景而言核心链路应尽量保持短小、扁平避免在同一个请求中重复加锁。如果业务逻辑确实复杂、存在嵌套调用需要重复加锁建议改用 Redisson 提供的可重入锁能力或者将需要互斥的代码抽到独立方法中统一在一处加锁释放从设计上降低死锁风险。七、Redisson 实战生产级分布式锁的正确打开方式7.1 为什么选择 Redisson前面手工实现 Redis 锁虽然能说明原理但在生产环境直接维护 Jedis 加锁、解锁、续期、可重入等代码成本很高且容易出错。Redisson 是基于 Redis 的 Java 驻内存数据网格框架它把分布式锁、同步器、集合、队列等能力做了完整封装尤其是分布式锁的实现非常成熟解决了锁续期、可重入、公平性、联锁、红锁等一系列工程问题。在秒杀项目中引入 Redisson 后开发者不再需要手工拼 Lua 解锁脚本也不用费心设计看门狗续期逻辑可以把更多精力放在库存扣减、防重复下单等业务规则上。7.2 引入依赖与基础配置在 Spring Boot 项目中引入 Redisson 通常先添加依赖再配置 RedissonClient Bean。下面是 Maven 依赖示例。dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.23.5/version /dependency也可以使用编程方式创建单机或集群客户端。下面以单机 Redis 为例。Config config new Config(); config.useSingleServer() .setAddress(redis://127.0.0.1:6379) .setPassword(your-password) .setConnectionPoolSize(64); RedissonClient redisson Redisson.create(config);7.3 可重入锁与 tryLock 实战Redisson 的 RLock 实现了 java.util.concurrent.locks.Lock 接口支持可重入。默认情况下Redisson 在加锁成功后启动看门狗机制每 10 秒自动续期到 30 秒只要业务没有执行完锁就不会过期。RLock lock redisson.getLock(lock:seckill: goodsId); boolean locked false; try { locked lock.tryLock(3, 30, TimeUnit.SECONDS); if (locked) { deductStock(userId, goodsId); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (locked lock.isHeldByCurrentThread()) { lock.unlock(); } }这里 tryLock 的第一个参数是等待时间第二个参数是锁的持有时间。如果手动指定了持有时间Redisson 不会自动续期所以建议在业务耗时可控时显式设置如果业务耗时不确定可以使用无参的 lock 或 tryLock 靠看门狗自动续期。7.4 公平锁、联锁与红锁的适用边界Redisson 还提供了公平锁、联锁和红锁。公平锁按请求到达顺序分配锁适合需要保证排队公平性的场景联锁可以把多个锁当作一个整体全部加锁成功才认为成功红锁通过多个独立 Redis 节点协商加锁用于降低主从切换带来的锁丢失风险。秒杀中商品库存锁通常使用普通可重入锁即可红锁在 Redis 高可用要求极高的交易场景才需要引入并且会带来额外延迟和运维复杂度。八、库存原子扣减Redis Lua 防超卖实战8.1 为什么把库存放进 Redis如果每次请求都去数据库查询并扣减库存数据库在秒杀瞬间会承受巨大压力。更合理的设计是活动开始前把库存数量预热到 Redis活动期间所有库存判断和扣减都在 Redis 中完成只有真正抢到商品的用户才异步落库生成订单。Redis 单线程执行 Lua 脚本可以保证查询、判断、扣减三个动作的原子性。8.2 Lua 脚本实现原子扣减下面是一段典型的库存扣减 Lua 脚本。它先读取当前库存判断是否大于 0再执行扣减并返回结果。local stockKey KEYS[1] local stock tonumber(redis.call(get, stockKey)) if stock nil then return -1 end if stock 0 then return 0 end redis.call(incrby, stockKey, -1) return 1返回 1 表示扣减成功返回 0 表示库存不足返回 -1 表示库存 Key 不存在可以触发活动状态检查或重新预热。8.3 预扣库存与异步落库高并发场景下通常采用预扣库存方案请求在 Redis 中扣减成功后即视作抢购成功随后发送 MQ 消息异步创建订单并扣减数据库库存。这样可以把数据库写入压力从秒杀峰值中剥离出来。需要注意的是异步链路必须保证最终一致性数据库库存扣减失败时要回补 Redis 库存订单超时要释放预扣同时通过对账任务兜底。九、缓存预热与热点 Key 治理9.1 活动开始前预热库存秒杀开始前应提前把商品库存、活动状态、商品基础信息加载到 Redis避免活动开始瞬间大量请求穿透缓存。预热可以由定时任务、活动发布事件或人工开关触发。public void preheatStock(String goodsId, int stock) { stringRedisTemplate.opsForValue().set(seckill:stock: goodsId, String.valueOf(stock)); }库存预热成功后还要确认活动状态 Key 一并写入例如 seckill:start:goodsId 和 seckill:end:goodsId减少活动开始时对数据库的查询。9.2 防止缓存击穿与热点 Key 压力缓存击穿是指热点 Key 过期瞬间大量请求同时打到数据库。秒杀商品库存 Key 如果意外过期也可能出现类似问题。解决方案包括活动期间库存 Key 不设置过期时间、使用互斥锁重建缓存、在极端流量下做逻辑过期等。当单个商品库存 Key 成为超级热点时还可以对热点 Key 做副本拆分把库存分散到多个分片中例如 stock:goodsId:0、stock:goodsId:1、stock:goodsId:2请求按用户 ID 哈希到某个分片扣减减少单 Key 压力。不过拆分后整体库存校验和售罄判断会更复杂需要权衡使用。十、防重复下单与接口幂等设计10.1 用户级抢购锁与 token 机制除了商品库存锁还应给用户加抢购锁防止同一用户重复提交。用户进入秒杀页时服务端下发一次性 token提交请求时携带 token。通过 Redis 的 SETNX 或 Lua 脚本校验并消耗 token可以天然实现幂等。public boolean tryDeductOnce(String userId, String goodsId) { String userKey seckill:user: goodsId : userId; Boolean ok stringRedisTemplate.opsForValue() .setIfAbsent(userKey, 1, Duration.ofMinutes(5)); return Boolean.TRUE.equals(ok); }同一用户同一商品第一次请求占位成功后续重复请求直接返回“请勿重复提交”。该记录在一定时间后过期但需要与订单状态配合避免用户因为首次请求失败而被长期拦截。10.2 数据库唯一索引兜底Redis 防重更多承担快速拦截数据库仍要建立唯一索引作为最终保障。例如订单表对用户 ID 和商品 ID 建立联合唯一索引即使 Redis 防重失效数据库也能拒绝重复插入保证最终数据不重复。十一、防黄牛限流与流量治理11.1 用户维度频率限制正常用户抢购频率有限而黄牛脚本往往发起高频请求。可以按 userId goodsId 设置访问频率超过阈值直接拒绝。public boolean allowRequest(String userId, String goodsId) { String rateKey seckill:rate: goodsId : userId; Long count stringRedisTemplate.opsForValue().increment(rateKey); if (count ! null count 1) { stringRedisTemplate.expire(rateKey, Duration.ofSeconds(1)); } return count ! null count 3; }这种固定窗口计数实现简单但边界处可能被突发流量击穿。要求更高时可以引入滑动窗口或令牌桶算法例如使用 Redisson 的 RateLimiter能够更平滑地控制请求通过速率。11.2 IP 与设备维度限流除了用户维度还应在网关或接入层对 IP、设备指纹做限流识别代理 IP、集群请求等异常行为。对同一 IP 短时间内的大量请求直接封禁或要求验证码能明显提高黄牛成本。前端按钮置灰、提交后禁用是基础体验要求但不能替代服务端校验。11.3 网关限流与削峰填谷秒杀流量最终到达服务前应在网关层使用令牌桶、漏桶等算法进行限流。配合消息队列把同步请求转化为异步请求可以把尖锐的流量峰值削平成后端可承受的流量曲线。限流应分层设计网关做粗粒度流量控制业务层做用户级和商品级精细控制数据库和下游依赖设置最后的兜底阈值。十二、完整秒杀实现与压测调优12.1 完整服务骨架下面给出一个精简但完整的秒杀服务骨架包含 Redis 库存预扣、防重、Redisson 锁和异步订单发送。Service public class SeckillService { Autowired private StringRedisTemplate redisTemplate; Autowired private RedissonClient redisson; Autowired private OrderProducer orderProducer; public SeckillResult seckill(String userId, String goodsId) { String stockKey seckill:stock: goodsId; String userKey seckill:user: goodsId : userId; Boolean first redisTemplate.opsForValue().setIfAbsent(userKey, 1, Duration.ofMinutes(5)); if (!Boolean.TRUE.equals(first)) return SeckillResult.repeated(); RLock lock redisson.getLock(lock:seckill: goodsId); boolean locked false; try { locked lock.tryLock(1, 10, TimeUnit.SECONDS); if (!locked) return SeckillResult.busy(); Long stock redisTemplate.opsForValue().decrement(stockKey); if (stock null || stock 0) { redisTemplate.opsForValue().increment(stockKey); return SeckillResult.soldOut(); } orderProducer.send(userId, goodsId); return SeckillResult.success(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return SeckillResult.fail(); } finally { if (locked lock.isHeldByCurrentThread()) lock.unlock(); } } }生产实现还会加入分布式事务、消息可靠性投递、订单状态查询、补偿任务和监控埋点但核心思路与上述骨架一致。12.2 压测与调优要点上线前必须进行压测重点观察目标 QPS 下的接口响应时间、错误率、Redis 与数据库水位。调优可以从几个方向入手缩短锁内逻辑把不必要的查询和日志移出临界区提升 Redis 连接池配置避免连接等待对库存 Key 和限流 Key 单独评估热点必要时拆分库存或引入本地缓存过滤售罄状态。调优不要只看单点还要看全链路。网关、负载均衡、应用线程池、Redis、MQ、数据库任意一环出现瓶颈都会反映为秒杀接口成功率下降。压测中记录每个环节的耗时和资源占用才能快速定位瓶颈。十三、总结从加锁到体系化治理借助 Redis 锁解决高并发秒杀问题核心并不只是学会 SETNX 或 Lua 脚本而是要理解锁在整体架构中的边界。Redis 锁负责保证库存扣减、重复点击等关键流程的并发正确性Redis 缓存负责承接读流量和保存热点库存限流、防刷、异步落库、消息队列、数据库唯一索引等机制共同保证系统的可用性与最终一致性。本文从单机锁的局限讲到 Redis 安全锁的实现再延伸到 Redisson 生产实践、库存原子扣减、缓存预热、接口幂等、防黄牛限流和完整压测调优。真正的生产方案一定不是靠某一种技巧包打天下而是把这些能力按业务目标编排起来形成分层、可监控、可降级的体系化架构。希望读者在掌握原理后能结合自己的业务场景把每一道防线都放到正确的位置上。
网站建设高端定制企业官网