IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> MySQL乐观锁解决库存超卖问题 -> 正文阅读

[大数据]MySQL乐观锁解决库存超卖问题

在通过多线程来解决高并发的问题上,线程安全往往是最先需要考虑的问题,其次才是性能。库存超卖问题是有很多种技术解决方案的,比如悲观锁,分布式锁,乐观锁,队列串行化,Redis原子操作等。本篇通过MySQL乐观锁来演示基本实现。

一、Goods和Order

@Data
public class Goods {
    private int id;
    private String name;
    private int stock;
    private int version;
}
@Data
public class Order {
    private int id;
    private int uid;
    private int gid;
}

二、OrderDao和GoodsDao

@Mapper
public interface OrderDao {

    /**
     * 插入订单
     * 注意: 由于order是sql中的关键字,所以表名需要加上反引号
     * @param order
     * @return int
     */
    @Insert("INSERT INTO `order` (uid, gid) VALUES (#{uid}, #{gid})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertOrder(Order order);
}
@Mapper
public interface GoodsDao {

    /**
     * 查询商品库存
     * @param id 商品id
     * @return
     */
    @Select("SELECT * FROM goods WHERE id = #{id}")
    Goods getStock(@Param("id") int id);

    /**
     * 乐观锁方案扣减库存
     * @param id 商品id
     * @param version 版本号
     * @return
     */
    @Update("UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = #{id} AND stock > 0 AND version = #{version}")
    int decreaseStockForVersion(@Param("id") int id, @Param("version") int version);
}

三、GoodsService

@Service
@Slf4j
public class GoodsService {

    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private OrderDao orderDao;

    /**
     * 扣减库存
     * @param gid 商品id
     * @param uid 用户id
     * @return SUCCESS 1 FAILURE 0
     */
    public int sellGoods(int gid, int uid) {
        int retryCount = 0;
        int update = 0;
        // 获取库存
        Goods goods = goodsDao.getStock(gid);
        if (goods.getStock() > 0) {
            // 乐观锁更新库存
            // 更新失败,说明其他线程已经修改过数据,本次扣减库存失败,可以重试一定次数或者返回
            // 最多重试3次
            while(retryCount < 3 && update == 0){
                update = this.reduceStock(gid);
                retryCount++;
            }
            if(update == 0){
                log.error("库存不足");
                return 0;
            }
            // 库存扣减成功,生成订单
            Order order = new Order();
            order.setUid(uid);
            order.setGid(gid);
            int result = orderDao.insertOrder(order);
            return result;
        }
        // 失败返回
        return 0;
    }


    /**
     * 减库存
     *
     * 由于默认的事务隔离级别是可重复读,会导致在同一个事务中查询3次goodsDao.getStock()
     * 得到的数据始终是相同的,所以需要提取reduceStock方法。每次循环都启动新的事务尝试扣减库存操作。
     */
    @Transactional(rollbackFor = Exception.class)
    public  int  reduceStock(int gid){
        int result = 0;
        //1、查询商品库存
        Goods goods = goodsDao.getStock(gid);
        //2、判断库存是否充足
        if(goods.getStock() >= 0){
            //3、减库存
            // 乐观锁更新库存
            result = goodsDao.decreaseStockForVersion(gid, goods.getVersion());
        }
        return result;
    }
}

四、单元测试GoodsServiceTest

@SpringBootTest
class GoodsServiceTest {

    @Autowired
    GoodsService goodsService;

    @Test
    void seckill() throws InterruptedException {

        // 库存初始化为10,这里通过CountDownLatch和线程池模拟100个并发
        int threadTotal = 100;

        ExecutorService executorService = Executors.newCachedThreadPool();

        final CountDownLatch countDownLatch = new CountDownLatch(threadTotal);
        for (int i = 0; i < threadTotal ; i++) {
            int uid = i;
            executorService.execute(() -> {
                try {
                    goodsService.sellGoods(1, uid);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }

        countDownLatch.await();
        executorService.shutdown();

    }
}

五、测试结果

库存由10减到了0,并且生产了10条订单记录。
在这里插入图片描述
在这里插入图片描述

参考资料
更新库存数量 - 乐观锁
通过乐观锁解决库存超卖的问题
mysql 乐观锁_使用MySQL乐观锁解决超卖问题
商品超买超卖问题分析及实战

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:43:14  更:2022-04-22 18:44:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:02:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码