网站建设谢辞公司装修效果全景图
2026/2/3 14:53:53 网站建设 项目流程
网站建设谢辞,公司装修效果全景图,做网站注意什么问题,找手工活做注册网站#x1f631; 前言#xff1a;一张票引发的血案 在单机应用中#xff0c;防止超卖很简单#xff0c;加个 synchronized 关键字就行。 但在微服务集群下#xff0c;synchronized 只能锁住当前机器的线程#xff0c;锁不住由于负载均衡分发到其他机器的请求。 超卖场景还原… 前言一张票引发的血案在单机应用中防止超卖很简单加个synchronized关键字就行。但在微服务集群下synchronized只能锁住当前机器的线程锁不住由于负载均衡分发到其他机器的请求。超卖场景还原库存剩 1 张。服务器 A的线程读库存stock 1。服务器 B的线程同时读库存stock 1。A 卖出stock 0。B 卖出stock 0。结果卖出了 2 张票实际上只有 1 张。这在铁路系统里意味着有人要站票或者上不了车属于 P0 级事故。我们需要一把**“分布式锁”**。 一、 为什么选 RedissonRedis 自带的setnx虽然能实现锁但有巨大缺陷死锁风险如果服务宕机锁没释放怎么办需要加过期时间过期时间难定业务执行了 10s锁 5s 就过期了导致锁失效锁误删。不可重入复杂的业务逻辑调用链无法多次拿锁。Redisson是 Redis 的 Java 驻内存数据网格它完美解决了上述问题特别是它的**“看门狗Watch Dog”**机制。Redisson 锁流程图 (Mermaid):Redisson 内部机制1. tryLock()加锁成功开启后台线程每 10s 续期业务结束停止看门狗加锁失败客户端请求Redis Master执行业务逻辑 看门狗 (Watch Dog)重置锁过期时间 (默认 30s)unlock() 释放锁自旋等待 / 放弃 二、 实战Redisson 锁住高铁票1. 引入依赖dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion3.23.0/version/dependency2. 抢票核心逻辑 (OrderService)我们不仅要加锁还要锁得细粒度。如果锁整个G1024车次那吞吐量太低。我们应该锁具体座次或车厢。ServicepublicclassTicketService{AutowiredprivateRedissonClientredissonClient;AutowiredprivateStringRedisTemplateredisTemplate;publicbooleanbuyTicket(StringtrainNumber,StringseatType){// 关键点锁的粒度。这里锁住特定车次的特定席别StringlockKeylock:ticket:trainNumber:seatType;RLocklockredissonClient.getLock(lockKey);try{// 1. 尝试获取锁// waitTime: 等待获取锁的时间leaseTime: -1 表示开启看门狗自动续期booleanisLockedlock.tryLock(5,-1,TimeUnit.SECONDS);if(isLocked){// 2. 双重检查 (Double Check) - 防止拿到锁之前库存被扣光// 这里不仅要查 Redis最稳妥是查数据库或预加载的缓存intstockgetStockFromCache(trainNumber,seatType);if(stock0){// 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先更库再删缓存 (延时双删)这是通用方案但在极端高并发下依然有脏数据风险。方案 BRedis 预扣减 (12306 推荐)真正的余票其实是以Redis 为准的。初始化将数据库库存预热到 Redis。扣减直接在 Redis 中扣减 (decr)。异步同步通过 MQ 异步将扣减结果同步回 MySQL做最终持久化。Redis Lua 脚本实现 (保证原子性):-- keys[1]: 库存 key-- argv[1]: 扣减数量localstocktonumber(redis.call(get,KEYS[1]))if(stocknil)thenreturn-1endif(stocktonumber(ARGV[1]))thenredis.call(decrby,KEYS[1],tonumber(ARGV[1]))return1elsereturn0endJava 调用// 这样就不需要 Redisson 锁住“读”操作只需要锁住“写”操作// 或者完全依赖 Redis 单线程特性连分布式锁都可以省去针对纯扣减逻辑LongresultredisTemplate.execute(script,Collections.singletonList(key),1);if(result1){// 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核心就在于对“共享资源”库存的争抢控制。Redisson 看门狗解决了锁过期导致的并发安全问题。Redis 预扣减 MQ解决了数据库的性能瓶颈和缓存一致性问题。分段锁解决了热点商品的单点瓶颈。Next Step:思考一下12306 还有一个极其复杂的逻辑区间票。比如北京 - 上海的车中间经停南京。如果我买北京 - 南京那么北京 - 上海的全程票库存也要减 1。这涉及到Bitmap (位图)技术。下一篇我们挑战用 Redis Bitmap 实现区间库存管理

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询