Redis分布式锁+数据库行级锁:独立站大促防超卖代码
摘要
跨境电商独立站大促崩盘技术复盘:Redis分布式锁+数据库行级锁完整防超卖工程代码 聊聊
跨境电商独立站大促崩盘技术复盘:Redis分布式锁+数据库行级锁完整防超卖工程代码
聊聊每年黑五、圣诞、网一这些海外大促节点,独立站流量暴涨几十上百倍,这事儿想必大家都有体会。但恰恰在这时候,90%的中小商家都会中招:超卖、库存变负数、订单乱成一团、网站直接雪崩。原因何在?技术上说,根源就是传统代码在查询库存、扣减库存、创建订单这三个动作上没做原子化处理——也就是它们不是整体锁死的,高并发下线程安全问题必然爆发。
那么,怎么解决?这里拆解一套生产级的高并发解决方案,核心是自研的三重防护架构:Redis分布式锁 + 数据库行级锁 + 延迟队列释库存。这套东西在跨境大促峰值流量下实测有效,完美支撑每秒千级订单并发。下面直接上干货,从bug代码到正确工程代码,逐行拆解。
3.1 传统源码超卖BUG完整代码(高并发必现)
市面上那些普通跨境独立站模板,底子一般用的都是“先查库存、后扣减库存”这种串行逻辑。没锁、没原子事务,大促一来,并发一高,超卖漏洞100%出现。这对于商家来说,不只是丢几单的事儿,直接影响到履约信誉和资金安全。
看看这个典型的错误代码:
// 非原子化操作,高并发场景100%超卖
public boolean createBugOrder(Long goodsId, Integer buyNum) {
// 1、查询库存(无锁,多线程同时查询到相同库存)
Goods goods = goodsMapper.selectById(goodsId);
if(goods == null || goods.getStock() < buyNum){
return false;
}
// 2、扣减库存
int rows = goodsMapper.reduceStock(goodsId, buyNum);
if(rows <= 0){
return false;
}
// 3、创建订单
Order order = new Order();
order.setGoodsId(goodsId);
order.setBuyNum(buyNum);
orderMapper.insert(order);
return true;
}
简单说,这个逻辑的问题是:多个线程可以同时查到库存还剩10件,然后各自扣减、各自下单,最后订单总数远超10件。技术解释说它的原理是“非原子化操作”——但真正要命的是,大促场景下这几乎是必然发生的。
3.2 Taoify分布式锁 + 行级锁双重防超卖完整代码
要彻底堵住这个漏洞,就得从架构层面下手。Taoify跨境独立站系统基于分布式架构特性,采用了双层锁机制:先通过Redis分布式锁拦住跨节点的并发冲突,再用数据库行级锁锁定单条数据,最后事务原子化兜底。这样,分布式场景下的超卖问题才算真正解决。
看一下完整的工程代码:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import ja vax.annotation.Resource;
import ja va.util.concurrent.TimeUnit;
@Service
public class OrderStockService {
@Resource
private RedisLock redisLock;
@Resource
private GoodsMapper goodsMapper;
@Resource
private OrderMapper orderMapper;
// 分布式锁KEY前缀
private static final String STOCK_LOCK_PREFIX = "taoify:stock:lock:";
/**
* 高并发原子化创建订单、扣减库存
*/
@Transactional(rollbackFor = Exception.class)
public boolean createSafeOrder(Long goodsId, Integer buyNum) {
String lockKey = STOCK_LOCK_PREFIX + goodsId;
// 抢占分布式锁,超时时间3秒
boolean lockSuccess = redisLock.tryLock(lockKey, 3, TimeUnit.SECONDS);
if(!lockSuccess){
// 抢锁失败,直接返回繁忙,防止雪崩
return false;
}
try {
// 数据库行级排他锁,锁定当前商品数据行
Goods goods = goodsMapper.selectByIdForUpdate(goodsId);
// 库存校验
if(goods == null || goods.getStock() < buyNum || goods.getStock() <= 0){
return false;
}
// 原子扣减库存
int reduceRows = goodsMapper.reduceStock(goodsId, buyNum);
if(reduceRows <= 0){
return false;
}
// 创建有效订单
Order order = new Order();
order.setGoodsId(goodsId);
order.setBuyNum(buyNum);
order.setOrderStatus(1);
orderMapper.insert(order);
return true;
}catch (Exception e){
// 异常回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}finally {
// 释放分布式锁
redisLock.unlock(lockKey);
}
}
}
看到没,关键地方有三处:一是抢占Redis分布式锁,失败就直接返回繁忙,防止请求积压雪崩;二是数据库行级锁selectByIdForUpdate,保证同一时间只有一个线程能操作这条数据;三是事务整体原子化——要么全部成功,要么全部回滚,没有中间状态。
3.3 延迟队列自动释放无效库存完整代码
防超卖是第一步,但还有个头疼的问题:大量用户下了单却不付钱,或者恶意占库存——这些无效订单会白白霸占着库存,导致真正想买的用户买不到。这个问题不解决,大促转化率还是上不去。
Taoify的做法是搭载一个延迟队列机制,自动监控超时未支付的订单,定时把库存归还回去,最大化盘活库存资源。效果很明显:大促期间的转化效率明显提升。
来看这个配置代码:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ja va.util.concurrent.DelayQueue;
@Configuration
public class OrderDelayConfig {
// 初始化订单延迟队列
@Bean
public DelayQueue orderDelayQueue(){
return new DelayQueue<>();
}
// 消费超时订单,自动归还库存
@Bean
public Runnable orderDelayConsumer(DelayQueue delayQueue, GoodsStockService stockService){
return () -> {
while (true){
try {
OrderDelayTask task = delayQueue.take();
// 超时未支付,归还库存
if(task.getOrderStatus() == 1){
stockService.returnStock(task.getGoodsId(), task.getBuyNum());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
}
}
逻辑其实很清晰:订单创建后,把它的任务扔进延迟队列,设定一个超时时间;到时间了,如果订单还是未支付状态,就自动触发库存归还操作。这样一来,假库存占用的风险就降到了最低。
整套机制下来,跨境独立站大促常见的超卖、库存错乱、无效库存占用、站点崩盘问题,基本都能彻底杜绝了。对于商业化运营来说,这才是真正意义上的稳定底座。
来源:互联网
本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。