高并发MySQL行锁应用层排队解决方案

前言
  • 在互联网项目中的最大挑战之一就是对于高并发的性能问题的处理,在流量小的情况下都可以正常运行的系统再高并发的情况都会暴露出比较严重的问题,并不是说前期的方案不好,而是需要优化甚至更复杂和精细的设计去迭代,例如查询问题可以通过缓存,大写入和峰值可以通过消息中间件,数据库可以分库分表,并且绝大部分性能问题都可以通过横向机器扩展去解决。
  • 其中有部分业务需要同步高并发写入,并且由于前期设计需要高并发流量竞争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的次数小于用户请求数,即创建了合并队列。这样就可以在应用层大量负载库存请求。

总结

这里可以在一定程度上抵抗高并发,但是仍然存在一些瓶颈,例如不好横向扩展,机器的增加会导致单机并发降低,机器太少当然也不行,因此需要根据并发量和机器数达到一个最佳比值,这个需要一定的调优,另一种方案就是读写流量分离,拿少量机器单独来做排队,与其他不需要走这个排队逻辑的接口物理上分开,压榨机器的极限。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MySQL中,是一种用于控制并发访问的机制。它可以防止多个事务同时修改或读取同一数据,以确保数据的一致性和完整性。 在高并发场景下,是非常重要的,因为它可以避免数据的竞争和冲突。当多个事务同时操作同一数据时,如果没有机制,可能会出现以下问题: 1. 脏读(Dirty Read):一个事务读取到了另一个事务未提交的数据。 2. 不可重复读(Non-repeatable Read):一个事务在多次读取同一数据时,发现数据不一致。 3. 幻读(Phantom Read):一个事务在多次查询同一范围的数据时,发现有新的数据满足了查询条件。 MySQL提供了两种的实现方式: 1. 共享(Shared Lock):也称为读,多个事务可以同时持有共享,用于防止其他事务进写操作。 2. 排他(Exclusive Lock):也称为写,只能有一个事务持有排他,用于防止其他事务进读或写操作。 在实际使用中,可以通过以下方式来使用提高高并发环境下的性能和数据完整性: 1. 仅在必要时使用会阻塞其他事务的访问,因此需要评估是否真正需要使用解决并发冲突。 2. 提高事务执效率:优化事务的逻辑和查询语句,减少事务持有的时间,从而减少对并发性能的影响。 3. 合理设置事务隔离级别:MySQL提供了多种事务隔离级别,如读未提交、读已提交、可重复读和串化。根据业务需求和数据一致性要求选择合适的隔离级别。 4. 使用索引:合理创建索引可以减少的范围,提高并发性能。 需要注意的是,的使用需要谨慎,过度使用或不当使用可能会导致死和性能问题。因此,在设计和实现时应该综合考虑业务需求、数据一致性和性能优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值