news 2026/4/17 18:32:42

仿 12306 售票系统:Spring Boot + Redisson 分布式锁解决“超卖”与“余票缓存一致性”难题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
仿 12306 售票系统:Spring Boot + Redisson 分布式锁解决“超卖”与“余票缓存一致性”难题

😱 前言:一张票引发的血案

在单机应用中,防止超卖很简单,加个synchronized关键字就行。
但在微服务集群下,synchronized只能锁住当前机器的线程,锁不住由于负载均衡分发到其他机器的请求。

超卖场景还原:
库存剩 1 张。

  1. 服务器 A的线程读库存:stock = 1
  2. 服务器 B的线程同时读库存:stock = 1
  3. A 卖出:stock = 0
  4. B 卖出:stock = 0
    结果:卖出了 2 张票,实际上只有 1 张。这在铁路系统里意味着有人要站票或者上不了车,属于 P0 级事故。

我们需要一把**“分布式锁”**。


🔒 一、 为什么选 Redisson?

Redis 自带的setnx虽然能实现锁,但有巨大缺陷:

  1. 死锁风险:如果服务宕机,锁没释放怎么办?(需要加过期时间)
  2. 过期时间难定:业务执行了 10s,锁 5s 就过期了,导致锁失效(锁误删)。
  3. 不可重入:复杂的业务逻辑调用链无法多次拿锁。

Redisson是 Redis 的 Java 驻内存数据网格,它完美解决了上述问题,特别是它的**“看门狗(Watch Dog)”**机制。

Redisson 锁流程图 (Mermaid):

Redisson 内部机制

1. tryLock()

加锁成功

开启后台线程

每 10s 续期

业务结束

停止看门狗

加锁失败

客户端请求

Redis Master

执行业务逻辑

🐶 看门狗 (Watch Dog)

重置锁过期时间 (默认 30s)

unlock() 释放锁

自旋等待 / 放弃


💻 二、 实战:Redisson 锁住高铁票

1. 引入依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.0</version></dependency>
2. 抢票核心逻辑 (OrderService)

我们不仅要加锁,还要锁得细粒度。如果锁整个G1024车次,那吞吐量太低。我们应该锁具体座次车厢

@ServicepublicclassTicketService{@AutowiredprivateRedissonClientredissonClient;@AutowiredprivateStringRedisTemplateredisTemplate;publicbooleanbuyTicket(StringtrainNumber,StringseatType){// 关键点:锁的粒度。这里锁住特定车次的特定席别StringlockKey="lock:ticket:"+trainNumber+":"+seatType;RLocklock=redissonClient.getLock(lockKey);try{// 1. 尝试获取锁// waitTime: 等待获取锁的时间,leaseTime: -1 表示开启看门狗自动续期booleanisLocked=lock.tryLock(5,-1,TimeUnit.SECONDS);if(isLocked){// 2. 双重检查 (Double Check) - 防止拿到锁之前库存被扣光// 这里不仅要查 Redis,最稳妥是查数据库或预加载的缓存intstock=getStockFromCache(trainNumber,seatType);if(stock>0){// 3. 扣减库存 (操作数据库 + 更新缓存)decreaseStock(trainNumber,seatType);createOrder();returntrue;}else{returnfalse;// 没票了}}else{returnfalse;// 系统繁忙 (获取锁失败)}}catch(InterruptedExceptione){returnfalse;}finally{// 4. 释放锁 (必须放在 finally 中)if(lock.isHeldByCurrentThread()){lock.unlock();}}}}

🔄 三、 难点攻克:余票缓存一致性

抢票时,用户疯狂刷新查看余票。如果每次都查数据库,数据库必死。我们必须查 Redis。
但这就引出了经典问题:数据库扣减了库存,Redis 里的缓存还没更新,怎么办?

在 12306 这种场景下,我们通常采用Cache-Aside Pattern (旁路缓存模式)的变种,并配合Lua 脚本

方案 A:先更库,再删缓存 (延时双删)

这是通用方案,但在极端高并发下依然有脏数据风险。

方案 B:Redis 预扣减 (12306 推荐)

真正的余票其实是以Redis 为准的。

  1. 初始化:将数据库库存预热到 Redis。
  2. 扣减:直接在 Redis 中扣减 (decr)。
  3. 异步同步:通过 MQ 异步将扣减结果同步回 MySQL,做最终持久化。

Redis Lua 脚本实现 (保证原子性):

-- keys[1]: 库存 key-- argv[1]: 扣减数量localstock=tonumber(redis.call('get',KEYS[1]))if(stock==nil)thenreturn-1endif(stock>=tonumber(ARGV[1]))thenredis.call('decrby',KEYS[1],tonumber(ARGV[1]))return1elsereturn0end

Java 调用:

// 这样就不需要 Redisson 锁住“读”操作,只需要锁住“写”操作// 或者完全依赖 Redis 单线程特性,连分布式锁都可以省去(针对纯扣减逻辑)Longresult=redisTemplate.execute(script,Collections.singletonList(key),"1");if(result==1){// Redis 扣减成功,发送 MQ 消息去异步更新 MySQLsendToMQ(orderInfo);}

🚀 四、 性能优化:分段锁 (Segment Lock)

如果 G1024 次列车只有一把锁,那么全中国想买这趟车的人都要排队。
我们可以借鉴ConcurrentHashMap的思想,将库存分段

假设二等座有 1000 张票:

  • Key1:stock:G1024:second:part1(0-100)
  • Key2:stock:G1024:second:part2(101-200)

用户请求进来时,随机路由到一个分段库存 Key 上。

  • 如果 Key1 有票,直接扣。
  • 如果 Key1 没票,尝试去 Key2 扣。

这样,并发度瞬间提升了 10 倍!


🎯 总结

开发一个简易版的 12306,核心就在于对“共享资源”(库存)的争抢控制。

  1. Redisson 看门狗:解决了锁过期导致的并发安全问题。
  2. Redis 预扣减 + MQ:解决了数据库的性能瓶颈和缓存一致性问题。
  3. 分段锁:解决了热点商品的单点瓶颈。

Next Step:
思考一下,12306 还有一个极其复杂的逻辑:区间票
比如北京 -> 上海的车,中间经停南京。如果我买北京 -> 南京,那么北京 -> 上海的全程票库存也要减 1。
这涉及到Bitmap (位图)技术。下一篇,我们挑战用 Redis Bitmap 实现区间库存管理!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/29 19:13:58

lora-scripts安全性考量:输入数据隐私保护措施

LoRA训练中的隐私防线&#xff1a;如何安全使用自动化脚本处理敏感数据 在生成式AI快速普及的今天&#xff0c;个性化模型定制已不再是大厂专属。LoRA&#xff08;Low-Rank Adaptation&#xff09;技术让普通开发者也能用几十张图片或几百条语料&#xff0c;就完成对Stable Dif…

作者头像 李华
网站建设 2026/3/31 6:20:52

C++开发者必看,C++26反射系统详解与实战应用

第一章&#xff1a;C26反射系统概述C26标准正在积极开发中&#xff0c;其中最受期待的特性之一是原生反射系统的引入。该系统旨在通过编译时获取类型信息的能力&#xff0c;极大提升元编程的表达力与可维护性&#xff0c;减少对模板技巧和宏的依赖。核心设计目标 支持在编译期查…

作者头像 李华
网站建设 2026/4/11 20:14:07

lora-scripts迁移学习能力验证:跨领域微调表现测试

LoRA微调实战&#xff1a;lora-scripts 跨领域迁移能力深度验证 在生成式AI快速普及的今天&#xff0c;一个现实问题日益凸显&#xff1a;通用大模型虽然强大&#xff0c;但面对特定风格、专业术语或品牌语义时&#xff0c;往往“懂个大概却不够精准”。比如你让Stable Diffusi…

作者头像 李华
网站建设 2026/3/26 19:45:02

多阶段训练方案:先预训练再精调的lora-scripts实现

多阶段训练方案&#xff1a;先预训练再精调的 LoRA 落地实践 在生成式 AI 爆发式发展的今天&#xff0c;我们早已不再满足于“通用模型随便画画、随便写写”的初级体验。无论是艺术创作者想复刻自己的画风&#xff0c;还是企业希望打造专属 IP 形象或行业知识问答系统&#xff…

作者头像 李华
网站建设 2026/4/12 18:36:28

打造企业专属营销文案机器人:lora-scripts微调LLM实战

打造企业专属营销文案机器人&#xff1a;lora-scripts微调LLM实战 在内容为王的时代&#xff0c;品牌每天都在与时间赛跑——新品发布要快、节日促销要准、社交媒体互动要“有梗”。可现实是&#xff0c;市场团队常常卡在文案创作上&#xff1a;资深运营离职后风格断层&#xf…

作者头像 李华
网站建设 2026/4/16 10:52:21

企业私有化部署lora-scripts训练系统的安全策略建议

企业私有化部署 lora-scripts 训练系统的安全策略建议 在医疗、金融和法律等高敏感行业&#xff0c;AI 模型的定制化需求日益增长——从构建专属客服话术到生成符合品牌调性的视觉内容。LoRA&#xff08;Low-Rank Adaptation&#xff09;因其参数高效、资源消耗低的特点&#x…

作者头像 李华