博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌
博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+MySQL+Vue等前后端分离项目,可以在左边的分类专栏找到更多项目。《Uniapp项目案例》有几个有uniapp教程,企业实战开发。《微服务实战》专栏是本人的实战经验总结,《Spring家族及微服务系列》专注Spring、SpringMVC、SpringBoot、SpringCloud系列、Nacos等源码解读、热门面试题、架构设计等。除此之外还有不少文章等你来细细品味,更多惊喜等着你哦
🍅uniapp微信小程序🍅面试题软考题免费使用,还可以使用微信支付,扫码加群。由于维护成本问题得不到解决,可能将停止线上维护。
🍅文末获取联系🍅精彩专栏推荐订阅👇🏻👇🏻 不然下次找不到哟
Java项目案例《100套》
https://blog.csdn.net/qq_57756904/category_12173599.html
uniapp小程序《100套》https://blog.csdn.net/qq_57756904/category_12173599.html
有需求代码永远写不完,而方法才是破解之道,抖音有实战视频课程,某马某千等培训都是2万左右,甚至广东有本科院校单单一年就得3万4年就12万学费,而且还没有包括吃饭的钱。所以很划算了。另外博客左侧有源码阅读专栏,对于求职有很大帮助,当然对于工作也是有指导意义等。在大城市求职,你面试来回一趟多多少少都在12块左右,而且一般不会一次性就通过,还得面试几家。而如果你对源码以及微服务等有深度认识,这无疑给你的面试添砖加瓦更上一层楼。
最后再送一句:最好是学会了,而不是学废了!!
2
在秒杀系统中,库存锁定是防止超卖的关键环节。以下是几种高效可靠的库存锁定方案:
一、Redis原子操作方案(推荐)
1. 基于DECR的原子扣减
public boolean lockStockWithRedis(Long itemId, int quantity) {
String key = "seckill:stock:" + itemId;
// Lua脚本保证原子性
String script =
"local stock = tonumber(redis.call('get', KEYS[1])) " +
"if stock >= tonumber(ARGV[1]) then " +
" return redis.call('decrby', KEYS[1], ARGV[1]) " +
"else " +
" return -1 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(key),
String.valueOf(quantity));
return result != null && result >= 0;
}
2. 分布式锁+库存扣减
public boolean lockStockWithDistributedLock(Long itemId, int quantity) {
String lockKey = "seckill:lock:" + itemId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,等待时间100ms,锁持有时间30s
if (lock.tryLock(100, 30000, TimeUnit.MILLISECONDS)) {
try {
String stockKey = "seckill:stock:" + itemId;
Long stock = redisTemplate.opsForValue().get(stockKey);
if (stock != null && stock >= quantity) {
redisTemplate.opsForValue().decrement(stockKey, quantity);
return true;
}
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
二、数据库方案
1. 乐观锁实现
@Update("UPDATE item_stock SET stock = stock - #{quantity}, version = version + 1 " +
"WHERE item_id = #{itemId} AND stock >= #{quantity} AND version = #{version}")
int deductStockWithOptimisticLock(@Param("itemId") Long itemId,
@Param("quantity") int quantity,
@Param("version") int version);
2. 悲观锁实现
@Select("SELECT * FROM item_stock WHERE item_id = #{itemId} FOR UPDATE")
ItemStock selectForUpdate(Long itemId);
public boolean lockStockWithPessimisticLock(Long itemId, int quantity) {
// 在事务中执行
ItemStock stock = stockMapper.selectForUpdate(itemId);
if (stock.getStock() >= quantity) {
stock.setStock(stock.getStock() - quantity);
stockMapper.update(stock);
return true;
}
return false;
}
三、预扣库存方案
1. 两阶段库存锁定
public boolean twoPhaseLockStock(Long itemId, int quantity, String orderNo) {
// 第一阶段:预扣Redis库存
if (!lockStockWithRedis(itemId, quantity)) {
return false;
}
// 第二阶段:异步持久化
mqTemplate.send("stock_prelock_topic", new StockLockEvent(itemId, quantity, orderNo));
return true;
}
// 消费者处理
public void handleStockLock(StockLockEvent event) {
try {
// 数据库最终扣减
int updated = stockMapper.realDeduct(event.getItemId(), event.getQuantity());
if (updated <= 0) {
// 回滚Redis
redisTemplate.opsForValue().increment(
"seckill:stock:" + event.getItemId(),
event.getQuantity());
// 标记订单失败
orderService.markAsFailed(event.getOrderNo());
}
} catch (Exception e) {
// 异常处理
}
}
四、库存分段方案(应对热点商品)
public boolean segmentLockStock(Long itemId, int quantity) {
// 将库存分成16个段
final int SEGMENTS = 16;
int segment = (int) (itemId % SEGMENTS);
String segmentKey = "seckill:stock:seg:" + itemId + ":" + segment;
// 每个段维护部分库存
Long remain = redisTemplate.opsForValue().decrement(segmentKey, quantity);
if (remain != null && remain >= 0) {
return true;
} else {
// 回滚
redisTemplate.opsForValue().increment(segmentKey, quantity);
return false;
}
}
五、库存回滚机制
public boolean rollbackStock(Long itemId, int quantity) {
String key = "seckill:stock:" + itemId;
Long result = redisTemplate.opsForValue().increment(key, quantity);
// 同时记录回滚流水
stockFlowMapper.insert(new StockFlow(itemId, "ROLLBACK", quantity));
return result != null;
}
六、库存状态检查
public StockLockResult checkStockLock(String orderNo) {
// 检查订单状态
Order order = orderMapper.selectByNo(orderNo);
if (order == null) {
return StockLockResult.notFound();
}
// 检查库存锁定状态
String lockKey = "seckill:lock:" + order.getItemId() + ":" + orderNo;
boolean locked = redisTemplate.hasKey(lockKey);
if (order.getStatus() == OrderStatus.PAID) {
return StockLockResult.success();
} else if (locked) {
return StockLockResult.locking();
} else {
return StockLockResult.failed();
}
}
七、方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Redis原子操作 | 性能高,实现简单 | 需要保证Redis高可用 | 绝大多数秒杀场景 |
乐观锁 | 无额外依赖 | 高并发下重试率高 | 并发量适中的场景 |
悲观锁 | 强一致性 | 性能差,可能死锁 | 对一致性要求极高的场景 |
预扣库存 | 用户体验好 | 实现复杂 | 需要快速响应的场景 |
库存分段 | 减少竞争 | 库存分配需合理 | 超级热点商品 |
最佳实践建议
组合使用:Redis原子操作 + 异步数据库更新 + 定时核对
监控报警:
库存锁定成功率
Redis库存与数据库库存差异
锁定/释放比例异常
压测验证:模拟峰值流量验证锁库存逻辑
兜底方案:
// 库存锁定兜底检查 @Scheduled(fixedRate = 60000) public void checkStockConsistency() { List<Item> items = itemMapper.selectAll(); items.forEach(item -> { Long redisStock = redisTemplate.opsForValue().get("seckill:stock:" + item.getId()); if (redisStock != null && !redisStock.equals(item.getStock())) { alarmService.notify("库存不一致:" + item.getId()); } }); }
通过以上方案,可以有效解决秒杀系统中的库存锁定问题,在保证系统高并发的条件下防止超卖现象。
3