首页 > 数据库 >分布式锁——JVM锁、MySQL锁解决多线程下并发争抢资源

分布式锁——JVM锁、MySQL锁解决多线程下并发争抢资源

时间:2024-03-10 21:12:44浏览次数:19  
标签:库存 goodsId version JVM MySQL Integer 多线程

分布式锁——JVM锁、MySQL锁解决库存超卖问题

引入库存扣案例

需求背景

电商项目中,用户购买商品后,会对商品的库存进行扣减。

需求实现

根据用户购买商品及商品数量,对商品的库存进行指定数量的扣减

public String deductStock(Long goodsId,Integer count){
        // 1.查询商品库存的数量
        Integer stock = stockDao.selectStockByGoodsId(goodsId);
        // 2.判断库存数量是否足够
        if(stock<count){
            return "库存不足";
        }

        //3. 如果库存数量足够,那么就去扣减库存
        stockDao.updateStockByGoodsId(goodsId,stock-count);
        return "库存扣减成功";
    }

使用JMeter进行压测,会产生无法扣减完库存

使用JVM锁

使用Synchronized

public synchronized String deductStock(Long goodsId,Integer count){
        // 1.查询商品库存的数量
        Integer stock = stockDao.selectStockByGoodsId(goodsId);
        // 2.判断库存数量是否足够
        if(stock<count){
            return "库存不足";
        }

        //3. 如果库存数量足够,那么就去扣减库存
        stockDao.updateStockByGoodsId(goodsId,stock-count);
        return "库存扣减成功";
    }

使用 ReentrantLock

 @Autowired
    private StockDao stockDao;
    ReentrantLock lock =new ReentrantLock();
    public String deductStock(Long goodsId,Integer count){
        lock.lock();
        try{
            // 1.查询商品库存的数量
            Integer stock = stockDao.selectStockByGoodsId(goodsId);
            // 2.判断库存数量是否足够
            if(stock<count){
                return "库存不足";
            }

            //3. 如果库存数量足够,那么就去扣减库存
            stockDao.updateStockByGoodsId(goodsId,stock-count);
        }finally {
            lock.unlock();
        }

        return "库存扣减成功";
    }

JVM失效场景有哪些

  • 多例模式下锁会失效

    image-20240309120437804

  • 事务模式,加锁在事务之前,否则事务扣减库存还未提交,另一个线程又获取到锁。

    • 可以将synchronized加到controller。解决该事务模式下问题

image-20240309183239789

image-20240309183305237

  • 集群模式:类似与独立模式下,当多个JVM时,无法用Synchronized锁住。

MySQL锁特性解决库存超卖问题

MySQL悲观锁—单条update语句问题

  • 易造成锁范围过大。
  • 无法在程序中获取扣减库存之前的库存值,比如将之前的库存数量进行打印和发送其他系统
  • 很多场景下无法满足其他业务诉求

image-20240309182644528

image-20240309182557071

MySQL forUpdate语句

  • 易造成锁范围过大
  • 性能较差
  • 思索问题
  • select for update 和普通 select语句读取内容不一致

image-20240309182624941

image-20240309182808973

MySQL乐观锁

MySQL乐观锁实现——版本号

实现方式

  • 给数据库表增加一列“version”
  • 读取数据的时候,将“version”字段一并读出
  • 数据每更新一次,“version”字段加1
  • 提交更新时,判断库中的“version”字段值和之前取出来的“version”比较
  • 相同更新,不相同重试

MySQL乐观锁实现——时间戳

实现方式

  • 给数据库表增加一列“timestamp”
  • 读取数据的时候,将“timestamp”字段一并读出
  • 数据每次更新一次,“timestamp”字段取出当前时间戳
  • 提交更新时,判断库中的“timestamp”字段值和之前取出来的“timestamp”比较
  • 相同更新,不相同重试

举例代码实现

Dao层代码

image-20240309182008647

Service层代码,需要一定的自旋(只有当version值匹配到才终止)

image-20240309182206866

MySQL乐观锁—问题

  • 高并发写操作下性能低,适应于读多写少的场景
  • 存在ABA问题

标签:库存,goodsId,version,JVM,MySQL,Integer,多线程
From: https://www.cnblogs.com/shine-rainbow/p/18064795

相关文章

  • flnkcdc+datastream实现mysql到mysql数据同步
    一、maven依赖<dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients</artifactId><version>1.18.1</version></dependency><dependency>......
  • 多进程、多线程知识再整理
    #threading模块'''cpython全局解释器锁导致同时只能有一个线程执行python,利用多cpu执行cpu密集型任务使用多进程,密集io型可以使用多线程并发classthreading.Thread(group=None,target=None,name=None,args=(),kwargs={},*,daemon=NoneThread类代表一个在独立控制线......
  • 手撕Java多线程(四)线程之间的协作
    线程之间的协作当多个线程可以一起去解决某个问题时,如果某些部分必须在其他部分之前完成,那么就需要对线程进行协调。join()在线程中调用另一个线程的join()方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。对于以下代码,虽然b线程先启动,但是因为在b线程中调用了a线程的join......
  • MySQL三种日志
    一、undolog(回滚日志)1.作用:(1)保证了事物的原子性(2)通过readview和undolog实现mvcc多版本并发控制2.在事物提交前,记录更新前的数据到undolog里,回滚的时候读取undolog来进行回滚3.undolog格式有一个rtx_id(上一次事物修改的id)和roll_ptr(指向需要回滚的版本)二、redolog1.......
  • IDEA使用与多线程
    IDEA缩写和快捷键psvm全称publicstaticvoidmainsout全称publicstaticvoidmainalt+enter处理异常s.out自动打印sctrl+art+t给整段代码加框如try-catch一、概念进程、程序和进程程序(program)是为完成任务、用某种语言编写的一组指令的集合。即指一段静态的代码,......
  • 第18章_MySQL8其它新特性
    第18章_MySQL8其它新特性讲师:尚硅谷-宋红康(江湖人称:康师傅)官网:http://www.atguigu.com1.MySQL8新特性概述MySQL从5.7版本直接跳跃发布了8.0版本,可见这是一个令人兴奋的里程碑版本。MySQL8版本在功能上做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是多......
  • MySQL基础篇快速记忆和查询
    查询语法:SELECT标识选择哪些列FROM标识从哪个表中选择去重(Distinct)在SELECT语句中使用关键字DISTINCT去除重复行SELECTDISTINCTdepartment_idFROMemployees;过滤(Where)语法:SELECT字段1,字段2FROM表名WHERE过滤条件使用WHERE子句,将不满足条......
  • conflucen引起mysql奔溃
    主要是mysql一直奔溃,受到confluence引起奔溃的提示解决办法:直接清空表:scheduler_run_details引起错误的语句:```insertintoscheduler_run_details(job_id,start_time,duration,outcome,message,id)values('SearchAuditListener','2024-03-1010:37:49.963',227,'......
  • spring 简单的使用 Hikari连接池 和 jdbc连接mysql 的一个简单例子
    pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://ma......
  • datax从mysql迁移数据到OceanBase
    datax部署下载dataxdatax下载地址安装dataxtar-zxvfdatax.tar.gz使用datax使用配置文件{"job":{"setting":{"speed":{"channel":4},"errorLimit":{......