前言
- 在互联网项目中的最大挑战之一就是对于高并发的性能问题的处理,在流量小的情况下都可以正常运行的系统再高并发的情况都会暴露出比较严重的问题,并不是说前期的方案不好,而是需要优化甚至更复杂和精细的设计去迭代,例如查询问题可以通过缓存,大写入和峰值可以通过消息中间件,数据库可以分库分表,并且绝大部分性能问题都可以通过横向机器扩展去解决。
- 其中有部分业务需要同步高并发写入,并且由于前期设计需要高并发流量竞争DB行锁,当然可以通过改变存储模型去解决,但是动静太大改动成本太高,在可预期的范围内又不需要抵抗如此大的流量,此次就是提供一种解决方案,案例使用库存扣减业务,在应用层进行用户请求的排队,避免大量的流量直接打入DB,导致连接池和行锁竞争导致DB性能降低。
代码
相关核心逻辑描述我直接写在注释里,因此后面的总结不再单独强调。这里需要说明的是,当用户线程超时的情况,用户请求拿到失败的结果,但是可能库存实际扣减,这个时候真实的上游会发出一个库存回退消息,让库存得到回退达到最终一致性,当然案例中省略了库存操作日志的写入,有日志的情况下才能完成回退消息的幂等。
/**
* @author : micro
* @date : 2020/8/5
* @description : 实现一个应用层排队秒杀逻辑
*/
public class KillService {
/**
* 模拟MySQL的库存记录
*/
public static Stock stock;
/**
* 秒杀的skuId
*/
private static Long killSkuId = 1L;
public static void main(String[] args) {
// 初始化库存数量
stock = new Stock(100000);
// 模拟并发
int concurrentCount = 40;
CountDownLatch countDownLatch = new CountDownLatch(concurrentCount);
KillService killService = new KillService();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(concurrentCount, concurrentCount, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10), new ThreadFactoryBuilder().setNameFormat("user-request-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < concurrentCount; i++) {
threadPoolExecutor.execute(() -> {
try {
/**
* countDownLatch模拟多个线程并发
*/
countDownLatch.countDown();
countDownLatch.await();
long start = System.currentTimeMillis();
// 发送并发请求
Result result = killService.commitRequest(killSkuId, RandomUtils.nextInt(1, 5));
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " 秒杀结果 : " + result.isSuccess() + ",信息:" + result.getMsg() + ",耗时:" + (end - start) + "ms");
stock.showOperateNumer();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
/**
* 并发计数
*/
Map<Long, AtomicInteger> counter = new ConcurrentHashMap<>();
/**
* 秒杀核心排队逻辑
* @param count
*/
private Result commitRequest(Long skuId, int count) {
System.out.println(Thread.currentThread().getName() + " 下单扣减:" + count);
Result result = new Result();
try {
// 累计阈值
incr(skuId);
// 判断是否达到并发阈值
if (killConcurrent(skuId)) {
KillPromise killPromise = addOrCreateSkuQueue(skuId, count);
try {
System.out.println(Thread.currentThread().getName() + " 挂起等待排队");
// 用户线程最多等待200ms
killPromise.waitPromise(200);
System.out.println(Thread.currentThread().getName() + " 被唤醒");
if (killPromise.isOk()) {
result.setSuccess(true);
} else {
result.setSuccess(false);
if (killPromise.getException() != null) {
result.setMsg(killPromise.getException().getMessage());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
result.setSuccess(false);
result.setMsg("超时");
}
return result;
}
// 如果没到阈值执行这里
// 普通单线程直接访问DB
if (! stock.duductStock(skuId, count)) {
result.setMsg("库存不足");
result.setSuccess(false);
return result;
}
result.setSuccess(true);
} catch (Exception e) {
e.printStackTrace();
result.setSuccess(false);
} finally {
// 释放阈值
decr(skuId);
}
return result;
}
private void decr(Long skuId) {
counter.get(skuId).decrementAndGet();
}
/**
* 避免并发递增
* @param skuId
*/
private synchronized void incr(Long skuId) {
AtomicInteger atomicInteger = counter.get(skuId);
if (atomicInteger == null) {
counter.put(skuId, new AtomicInteger(1));
} else {
atomicInteger.incrementAndGet();
}
}
/**
* sku-扣减库存请求
*/
Map<Long, MergeRequestWorker> mergeRequestWorkerMap = new ConcurrentHashMap<>();
private Object lock = new Object();
/**
* 如果秒杀队列不存在就创建,否则直接加入队列
*/
private KillPromise addOrCreateSkuQueue(Long skuId, int count) {
StockParam stockParam = new StockParam();
KillPromise killPromise = new KillPromise();
killPromise.setRequestThread(Thread.currentThread());
stockParam.setKillPromise(killPromise);
stockParam.setSkuId(skuId);
stockParam.setCount(count);
// 避免并发创建
synchronized (lock) {
MergeRequestWorker mergeRequestWorker = mergeRequestWorkerMap.get(skuId);
if (mergeRequestWorker == null) {
System.out.println(Thread.currentThread().getName() + " : ->创建新队列");
mergeRequestWorker = new MergeRequestWorker();
mergeRequestWorkerMap.put(skuId, mergeRequestWorker);
} else {
System.out.println(Thread.currentThread().getName() + ", 已存在队列,加入队列");
}
mergeRequestWorker.createOrAddStockParam(stockParam);
}
return killPromise;
}
/**
* 判断是否创建队列的阈值
*/
private boolean killConcurrent(Long skuId) {
return Optional.ofNullable(counter.get(skuId)).orElse(new AtomicInteger(0)).get() > 2;
}
}
/**
* 队列封装
* 单线程轮训合并用户请求并执行
*/
class MergeRequestWorker implements Runnable {
/**
* 用户线程队列
*/
LinkedBlockingQueue<StockParam> stockParamQueue = new LinkedBlockingQueue<>(10);;
public void createOrAddStockParam(StockParam stockParam) {
stockParamQueue.add(stockParam);
// 起步执行轮训队列
Thread thread = new Thread(this);
thread.setName("merge-request-" + System.currentTimeMillis() % 100 + RandomUtils.nextInt(0, 100));
thread.start();
}
@Override
public void run() {
List<StockParam> stockParamList = new ArrayList<>();
while (true) {
try {
// 队列为空等待100ms
if (stockParamQueue.isEmpty()) {
Thread.sleep(100);
if (stockParamQueue.isEmpty()) {
return ;
}
}
int size = 0;
while (true) {
StockParam poll = stockParamQueue.poll();
// 10个请求一起合并
if (poll != null && size <= 10) {
stockParamList.add(poll);
} else {
// 队列为空或则达到最大批量,先执行
break;
}
}
if (stockParamList.isEmpty()) {
continue;
}
System.out.println(Thread.currentThread().getName() + " , 合并线程 : " + stockParamList);
mergeOperateStock(stockParamList);
stockParamList.clear();
} catch (InterruptedException e) {
if (! stockParamList.isEmpty()) {
stockParamList.forEach(param -> {
param.getKillPromise().setOk(false);
param.getKillPromise().notifyRequest();
});
}
}
}
}
/**
* 合并执行用户多个线程
* @param stockParamList
*/
private void mergeOperateStock(List<StockParam> stockParamList) {
Long skuId = stockParamList.get(0).getSkuId();
// 合并数量
Integer count = stockParamList.stream().mapToInt(StockParam::getCount).sum();
boolean success = KillService.stock.duductStock(skuId, count);
if (success) {
// 合并成功
System.out.println(Thread.currentThread().getName() + " 合并扣减成功,通知用户线程");
stockParamList.forEach(e -> e.getKillPromise().notifyRequest());
} else {
// 合并不成功,可以拆分用户线程分别执行
// 按照扣减数量依次扣减
System.out.println(Thread.currentThread().getName() + " 开始合并后的单独扣减");
// 排序,让扣减数量少的先扣减,尽量满足最多请求量成功
stockParamList.sort((s1, s2) -> {
if (s1.getCount() < s2.getCount()) {
return -1;
} else if (s1.getCount() > s2.getCount()) {
return 1;
}
return 0;
});
/*
逐个执行用户线程,退化为单线程
*/
for (StockParam stockParam : stockParamList) {
if (KillService.stock.duductStock(stockParam.getSkuId(), stockParam.getCount())) {
stockParam.getKillPromise().notifyRequest();
} else {
KillPromise killPromise = stockParam.getKillPromise();
killPromise.setException(new Exception("合并逐序扣减库存不足"));
killPromise.setOk(false);
System.out.println(Thread.currentThread().getName() + " 合并逐序扣减库存不足 : " + stockParam);
stockParam.getKillPromise().notifyRequest();
}
}
}
}
}
/**
* 队列元素
*/
@Data
class StockParam {
/**
* 用户线程引用
*/
private KillPromise killPromise;
/**
* 目标skuId
*/
private Long skuId;
/**
* 扣减数量
*/
private Integer count;
@Override
public String toString() {
return "StockParam{" +
"killPromise=" + killPromise +
", skuId=" + skuId +
", count=" + count +
'}';
}
}
/**
* 合并线程对用户线程的返回
*/
@Data
class KillPromise {
private Exception exception;
private boolean ok = true;
private Thread requestThread;
public synchronized void notifyRequest() {
super.notify();
}
@Override
public String toString() {
return "KillPromise{" +
"exception=" + exception +
", ok=" + ok +
", requestThread=" + requestThread +
'}';
}
/**
* 用户线程阻塞等待
*/
public synchronized void waitPromise(int maxWaitTime) throws InterruptedException {
wait(maxWaitTime);
}
}
/**
* 封装秒杀扣减库存的结果
*/
@Data
class Result {
private boolean success;
private String msg;
}
/**
* 模拟MySQL的InnoDB索引更新库存的行锁
*/
@Data
class Stock {
private Long skuId = 1L;
/**
* 剩余库存
*/
private Integer availCount;
/**
* 操作次数
*/
private int operteNumer;
public Stock(Integer availCount) {
this.availCount = availCount;
}
/**
* 扣减库存, synchronized 关键字模拟行锁
* 模拟只有库存不足才会返回false
*/
public synchronized boolean duductStock(Long skuId, Integer count) {
operteNumer ++;
System.out.println(Thread.currentThread().getName() + " : 剩余库存 : " + this.availCount + ",扣减:" + count);
if (this.availCount >= count && this.skuId.equals(skuId)) {
this.availCount = availCount - count;
return true;
}
return false;
}
public void showOperateNumer() {
System.out.println(Thread.currentThread().getName() + " : 一共操作了:" + operteNumer + "次库存记录, 剩余库存 : " + this.availCount);
}
}
user-request-16 下单扣减:3
user-request-30 下单扣减:1
user-request-3 下单扣减:2
user-request-5 下单扣减:1
user-request-31 下单扣减:4
user-request-4 下单扣减:2
user-request-28 下单扣减:4
user-request-29 下单扣减:1
user-request-27 下单扣减:1
user-request-26 下单扣减:3
user-request-6 下单扣减:2
user-request-25 下单扣减:4
user-request-15 下单扣减:4
user-request-24 下单扣减:1
user-request-7 下单扣减:4
user-request-39 下单扣减:4
user-request-9 下单扣减:4
user-request-22 下单扣减:2
user-request-10 下单扣减:1
user-request-11 下单扣减:1
user-request-23 下单扣减:2
user-request-14 下单扣减:3
user-request-12 下单扣减:1
user-request-8 下单扣减:1
user-request-18 下单扣减:3
user-request-20 下单扣减:1
user-request-19 下单扣减:2
user-request-13 下单扣减:3
user-request-17 下单扣减:1
user-request-21 下单扣减:3
user-request-37 下单扣减:1
user-request-36 下单扣减:2
user-request-0 下单扣减:2
user-request-1 下单扣减:3
user-request-35 下单扣减:4
user-request-34 下单扣减:2
user-request-33 下单扣减:2
user-request-2 下单扣减:1
user-request-38 下单扣减:3
user-request-32 下单扣减:1
user-request-8 : ->创建新队列
user-request-8 挂起等待排队
user-request-32, 已存在队列,加入队列
user-request-32 挂起等待排队
merge-request-8872 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-8,5,main]}, skuId=1, count=1}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-32,5,main]}, skuId=1, count=1}]
user-request-38, 已存在队列,加入队列
user-request-38 挂起等待排队
user-request-11, 已存在队列,加入队列
merge-request-8951 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-38,5,main]}, skuId=1, count=3}]
user-request-11 挂起等待排队
user-request-14, 已存在队列,加入队列
merge-request-8951 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-11,5,main]}, skuId=1, count=1}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-14,5,main]}, skuId=1, count=3}]
user-request-10, 已存在队列,加入队列
user-request-14 挂起等待排队
merge-request-8919 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-10,5,main]}, skuId=1, count=1}]
user-request-10 挂起等待排队
user-request-22, 已存在队列,加入队列
user-request-22 挂起等待排队
user-request-12, 已存在队列,加入队列
merge-request-890 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-22,5,main]}, skuId=1, count=2}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-12,5,main]}, skuId=1, count=1}]
user-request-12 挂起等待排队
user-request-9, 已存在队列,加入队列
user-request-9 挂起等待排队
user-request-23, 已存在队列,加入队列
merge-request-9076 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-9,5,main]}, skuId=1, count=4}]
user-request-23 挂起等待排队
user-request-2, 已存在队列,加入队列
merge-request-9084 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-23,5,main]}, skuId=1, count=2}]
user-request-2 挂起等待排队
user-request-18, 已存在队列,加入队列
merge-request-9066 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-2,5,main]}, skuId=1, count=1}]
user-request-18 挂起等待排队
user-request-15, 已存在队列,加入队列
merge-request-9052 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-18,5,main]}, skuId=1, count=3}]
user-request-15 挂起等待排队
user-request-39, 已存在队列,加入队列
merge-request-9177 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-15,5,main]}, skuId=1, count=4}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-39,5,main]}, skuId=1, count=4}]
user-request-7, 已存在队列,加入队列
user-request-39 挂起等待排队
merge-request-9131 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-7,5,main]}, skuId=1, count=4}]
user-request-7 挂起等待排队
user-request-3, 已存在队列,加入队列
user-request-3 挂起等待排队
user-request-33, 已存在队列,加入队列
merge-request-9258 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-3,5,main]}, skuId=1, count=2}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-33,5,main]}, skuId=1, count=2}]
user-request-33 挂起等待排队
user-request-20, 已存在队列,加入队列
user-request-20 挂起等待排队
user-request-25, 已存在队列,加入队列
merge-request-9227 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-20,5,main]}, skuId=1, count=1}]
user-request-25 挂起等待排队
user-request-24, 已存在队列,加入队列
merge-request-9372 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-25,5,main]}, skuId=1, count=4}]
user-request-24 挂起等待排队
merge-request-9326 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-24,5,main]}, skuId=1, count=1}]
user-request-28, 已存在队列,加入队列
user-request-28 挂起等待排队
user-request-34, 已存在队列,加入队列
merge-request-9382 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-28,5,main]}, skuId=1, count=4}, StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-34,5,main]}, skuId=1, count=2}]
user-request-34 挂起等待排队
user-request-19, 已存在队列,加入队列
merge-request-9463 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-19,5,main]}, skuId=1, count=2}]
user-request-19 挂起等待排队
user-request-4, 已存在队列,加入队列
merge-request-9441 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-4,5,main]}, skuId=1, count=2}]
user-request-4 挂起等待排队
user-request-31, 已存在队列,加入队列
merge-request-9433 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-31,5,main]}, skuId=1, count=4}]
user-request-31 挂起等待排队
user-request-13, 已存在队列,加入队列
user-request-13 挂起等待排队
merge-request-9433 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-13,5,main]}, skuId=1, count=3}]
user-request-30, 已存在队列,加入队列
merge-request-9433 : 剩余库存 : 100000,扣减:3
merge-request-9433 合并扣减成功,通知用户线程
user-request-30 挂起等待排队
user-request-35, 已存在队列,加入队列
merge-request-959 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-30,5,main]}, skuId=1, count=1}]
merge-request-9326 : 剩余库存 : 99997,扣减:1
merge-request-8951 : 剩余库存 : 99996,扣减:4
user-request-26, 已存在队列,加入队列
merge-request-9326 合并扣减成功,通知用户线程
merge-request-8951 合并扣减成功,通知用户线程
merge-request-9076 : 剩余库存 : 99992,扣减:4
merge-request-9076 合并扣减成功,通知用户线程
user-request-26 挂起等待排队
user-request-13 被唤醒
user-request-11 被唤醒
user-request-35 挂起等待排队
user-request-11 秒杀结果 : true,信息:null,耗时:17ms
user-request-13 秒杀结果 : true,信息:null,耗时:17ms
user-request-24 被唤醒
user-request-14 被唤醒
user-request-9 被唤醒
user-request-16, 已存在队列,加入队列
merge-request-9258 : 剩余库存 : 99988,扣减:4
merge-request-9433 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-26,5,main]}, skuId=1, count=3}]
merge-request-9517 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-35,5,main]}, skuId=1, count=4}]
merge-request-9258 合并扣减成功,通知用户线程
user-request-29, 已存在队列,加入队列
user-request-3 被唤醒
user-request-3 秒杀结果 : true,信息:null,耗时:17ms
merge-request-8872 : 剩余库存 : 99984,扣减:2
user-request-9 秒杀结果 : true,信息:null,耗时:17ms
user-request-29 挂起等待排队
user-request-14 秒杀结果 : true,信息:null,耗时:17ms
user-request-24 秒杀结果 : true,信息:null,耗时:17ms
user-request-13 : 一共操作了:5次库存记录, 剩余库存 : 99988
user-request-11 : 一共操作了:5次库存记录, 剩余库存 : 99988
user-request-24 : 一共操作了:7次库存记录, 剩余库存 : 99982
user-request-14 : 一共操作了:7次库存记录, 剩余库存 : 99982
user-request-9 : 一共操作了:7次库存记录, 剩余库存 : 99982
user-request-0, 已存在队列,加入队列
merge-request-9066 : 剩余库存 : 99982,扣减:1
merge-request-8872 合并扣减成功,通知用户线程
user-request-3 : 一共操作了:6次库存记录, 剩余库存 : 99984
user-request-5, 已存在队列,加入队列
merge-request-9258 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-29,5,main]}, skuId=1, count=1}]
user-request-33 被唤醒
user-request-33 秒杀结果 : true,信息:null,耗时:18ms
user-request-6, 已存在队列,加入队列
merge-request-9647 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-16,5,main]}, skuId=1, count=3}]
user-request-16 挂起等待排队
merge-request-9738 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-6,5,main]}, skuId=1, count=2}]
user-request-37, 已存在队列,加入队列
user-request-33 : 一共操作了:8次库存记录, 剩余库存 : 99981
user-request-5 挂起等待排队
merge-request-9788 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-5,5,main]}, skuId=1, count=1}]
merge-request-8872 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-0,5,main]}, skuId=1, count=2}]
user-request-0 挂起等待排队
user-request-32 被唤醒
user-request-8 被唤醒
merge-request-959 : 剩余库存 : 99981,扣减:1
merge-request-9066 合并扣减成功,通知用户线程
merge-request-9227 : 剩余库存 : 99980,扣减:1
merge-request-959 合并扣减成功,通知用户线程
user-request-8 秒杀结果 : true,信息:null,耗时:18ms
user-request-32 秒杀结果 : true,信息:null,耗时:18ms
user-request-1, 已存在队列,加入队列
user-request-37 挂起等待排队
merge-request-9724 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-37,5,main]}, skuId=1, count=1}]
user-request-6 挂起等待排队
merge-request-9771 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-1,5,main]}, skuId=1, count=3}]
user-request-27, 已存在队列,加入队列
user-request-1 挂起等待排队
user-request-32 : 一共操作了:10次库存记录, 剩余库存 : 99979
user-request-8 : 一共操作了:10次库存记录, 剩余库存 : 99979
user-request-30 被唤醒
merge-request-8919 : 剩余库存 : 99979,扣减:1
merge-request-9227 合并扣减成功,通知用户线程
user-request-2 被唤醒
user-request-20 被唤醒
merge-request-8951 : 剩余库存 : 99978,扣减:3
merge-request-8919 合并扣减成功,通知用户线程
user-request-17, 已存在队列,加入队列
user-request-27 挂起等待排队
user-request-30 秒杀结果 : true,信息:null,耗时:19ms
user-request-10 被唤醒
user-request-30 : 一共操作了:12次库存记录, 剩余库存 : 99975
merge-request-9441 : 剩余库存 : 99975,扣减:2
merge-request-8951 合并扣减成功,通知用户线程
user-request-20 秒杀结果 : true,信息:null,耗时:19ms
merge-request-9227 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-27,5,main]}, skuId=1, count=1}]
user-request-2 秒杀结果 : true,信息:null,耗时:19ms
user-request-20 : 一共操作了:13次库存记录, 剩余库存 : 99973
user-request-38 被唤醒
merge-request-9861 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-17,5,main]}, skuId=1, count=1}]
merge-request-9131 : 剩余库存 : 99973,扣减:4
merge-request-9441 合并扣减成功,通知用户线程
user-request-36, 已存在队列,加入队列
user-request-17 挂起等待排队
user-request-10 秒杀结果 : true,信息:null,耗时:19ms
user-request-4 被唤醒
user-request-36 挂起等待排队
merge-request-9052 : 剩余库存 : 99969,扣减:3
merge-request-9131 合并扣减成功,通知用户线程
user-request-38 秒杀结果 : true,信息:null,耗时:19ms
user-request-2 : 一共操作了:13次库存记录, 剩余库存 : 99973
user-request-38 : 一共操作了:15次库存记录, 剩余库存 : 99966
user-request-7 被唤醒
merge-request-9925 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-36,5,main]}, skuId=1, count=2}]
merge-request-9433 : 剩余库存 : 99966,扣减:4
merge-request-9052 合并扣减成功,通知用户线程
merge-request-9084 : 剩余库存 : 99962,扣减:2
user-request-4 秒杀结果 : true,信息:null,耗时:20ms
user-request-4 : 一共操作了:17次库存记录, 剩余库存 : 99960
user-request-21, 已存在队列,加入队列
user-request-10 : 一共操作了:14次库存记录, 剩余库存 : 99969
merge-request-9463 : 剩余库存 : 99960,扣减:2
merge-request-9084 合并扣减成功,通知用户线程
merge-request-9382 : 剩余库存 : 99958,扣减:6
user-request-18 被唤醒
merge-request-9433 合并扣减成功,通知用户线程
user-request-7 秒杀结果 : true,信息:null,耗时:20ms
user-request-7 : 一共操作了:19次库存记录, 剩余库存 : 99952
user-request-31 被唤醒
user-request-18 秒杀结果 : true,信息:null,耗时:20ms
user-request-18 : 一共操作了:19次库存记录, 剩余库存 : 99952
merge-request-9084 , 合并线程 : [StockParam{killPromise=KillPromise{exception=null, ok=true, requestThread=Thread[user-request-21,5,main]}, skuId=1, count=3}]
merge-request-9925 : 剩余库存 : 99952,扣减:2
merge-request-9382 合并扣减成功,通知用户线程
merge-request-9861 : 剩余库存 : 99950,扣减:1
user-request-23 被唤醒
user-request-21 挂起等待排队
merge-request-9463 合并扣减成功,通知用户线程
user-request-23 秒杀结果 : true,信息:null,耗时:21ms
user-request-23 : 一共操作了:21次库存记录, 剩余库存 : 99949
merge-request-9227 : 剩余库存 : 99949,扣减:1
merge-request-9861 合并扣减成功,通知用户线程
user-request-34 被唤醒
user-request-28 被唤醒
merge-request-9925 合并扣减成功,通知用户线程
user-request-31 秒杀结果 : true,信息:null,耗时:20ms
user-request-36 被唤醒
user-request-28 秒杀结果 : true,信息:null,耗时:21ms
user-request-34 秒杀结果 : true,信息:null,耗时:21ms
user-request-17 被唤醒
merge-request-9771 : 剩余库存 : 99948,扣减:3
merge-request-9227 合并扣减成功,通知用户线程
user-request-19 被唤醒
user-request-27 被唤醒
merge-request-9724 : 剩余库存 : 99945,扣减:1
merge-request-9771 合并扣减成功,通知用户线程
user-request-17 秒杀结果 : true,信息:null,耗时:21ms
user-request-34 : 一共操作了:22次库存记录, 剩余库存 : 99948
user-request-28 : 一共操作了:22次库存记录, 剩余库存 : 99948
user-request-36 秒杀结果 : true,信息:null,耗时:21ms
user-request-36 : 一共操作了:24次库存记录, 剩余库存 : 99944
user-request-31 : 一共操作了:22次库存记录, 剩余库存 : 99948
user-request-17 : 一共操作了:24次库存记录, 剩余库存 : 99944
user-request-1 被唤醒
user-request-1 秒杀结果 : true,信息:null,耗时:22ms
merge-request-8872 : 剩余库存 : 99944,扣减:2
merge-request-9724 合并扣减成功,通知用户线程
merge-request-9788 : 剩余库存 : 99942,扣减:1
user-request-37 被唤醒
merge-request-9738 : 剩余库存 : 99941,扣减:2
user-request-27 秒杀结果 : true,信息:null,耗时:22ms
merge-request-9647 : 剩余库存 : 99939,扣减:3
user-request-19 秒杀结果 : true,信息:null,耗时:22ms
merge-request-9258 : 剩余库存 : 99936,扣减:1
merge-request-9647 合并扣减成功,通知用户线程
user-request-27 : 一共操作了:27次库存记录, 剩余库存 : 99939
merge-request-9738 合并扣减成功,通知用户线程
user-request-37 秒杀结果 : true,信息:null,耗时:22ms
merge-request-9788 合并扣减成功,通知用户线程
merge-request-8872 合并扣减成功,通知用户线程
user-request-1 : 一共操作了:24次库存记录, 剩余库存 : 99944
user-request-0 被唤醒
user-request-5 被唤醒
user-request-37 : 一共操作了:29次库存记录, 剩余库存 : 99935
user-request-5 秒杀结果 : true,信息:null,耗时:22ms
user-request-6 被唤醒
user-request-16 被唤醒
merge-request-9517 : 剩余库存 : 99935,扣减:4
merge-request-9258 合并扣减成功,通知用户线程
user-request-19 : 一共操作了:28次库存记录, 剩余库存 : 99936
user-request-29 被唤醒
merge-request-9433 : 剩余库存 : 99931,扣减:3
merge-request-9517 合并扣减成功,通知用户线程
user-request-16 秒杀结果 : true,信息:null,耗时:23ms
user-request-6 秒杀结果 : true,信息:null,耗时:23ms
user-request-5 : 一共操作了:29次库存记录, 剩余库存 : 99935
user-request-0 秒杀结果 : true,信息:null,耗时:22ms
user-request-6 : 一共操作了:31次库存记录, 剩余库存 : 99928
user-request-16 : 一共操作了:31次库存记录, 剩余库存 : 99928
user-request-35 被唤醒
merge-request-9372 : 剩余库存 : 99928,扣减:4
merge-request-9433 合并扣减成功,通知用户线程
user-request-29 秒杀结果 : true,信息:null,耗时:23ms
user-request-26 被唤醒
merge-request-9177 : 剩余库存 : 99924,扣减:8
merge-request-9372 合并扣减成功,通知用户线程
user-request-35 秒杀结果 : true,信息:null,耗时:23ms
user-request-0 : 一共操作了:31次库存记录, 剩余库存 : 99928
user-request-35 : 一共操作了:33次库存记录, 剩余库存 : 99916
user-request-25 被唤醒
merge-request-890 : 剩余库存 : 99916,扣减:3
merge-request-9177 合并扣减成功,通知用户线程
user-request-26 秒杀结果 : true,信息:null,耗时:23ms
user-request-29 : 一共操作了:32次库存记录, 剩余库存 : 99924
user-request-26 : 一共操作了:34次库存记录, 剩余库存 : 99913
user-request-39 被唤醒
user-request-15 被唤醒
merge-request-9084 : 剩余库存 : 99913,扣减:3
merge-request-890 合并扣减成功,通知用户线程
user-request-25 秒杀结果 : true,信息:null,耗时:23ms
user-request-12 被唤醒
user-request-22 被唤醒
merge-request-9084 合并扣减成功,通知用户线程
user-request-15 秒杀结果 : true,信息:null,耗时:23ms
user-request-39 秒杀结果 : true,信息:null,耗时:23ms
user-request-15 : 一共操作了:34次库存记录, 剩余库存 : 99910
user-request-21 被唤醒
user-request-22 秒杀结果 : true,信息:null,耗时:24ms
user-request-12 秒杀结果 : true,信息:null,耗时:23ms
user-request-25 : 一共操作了:34次库存记录, 剩余库存 : 99910
user-request-12 : 一共操作了:34次库存记录, 剩余库存 : 99910
user-request-22 : 一共操作了:34次库存记录, 剩余库存 : 99910
user-request-21 秒杀结果 : true,信息:null,耗时:24ms
user-request-39 : 一共操作了:34次库存记录, 剩余库存 : 99910
user-request-21 : 一共操作了:34次库存记录, 剩余库存 : 99910
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
这里模拟了多个个线程秒杀的情况,你也可以灵活更高库存的初始值验证此方案的可行性,分析结果可以发现没有超卖,请求到DB的次数小于用户请求数,即创建了合并队列。这样就可以在应用层大量负载库存请求。
总结
这里可以在一定程度上抵抗高并发,但是仍然存在一些瓶颈,例如不好横向扩展,机器的增加会导致单机并发降低,机器太少当然也不行,因此需要根据并发量和机器数达到一个最佳比值,这个需要一定的调优,另一种方案就是读写流量分离,拿少量机器单独来做排队,与其他不需要走这个排队逻辑的接口物理上分开,压榨机器的极限。