• 分布式锁之mysql 锁


    使用数据锁:悲观锁 或者 乐观锁

    1. 一个sql:直接更新时判断,在更新中判断库存是否大于0

      update table set surplus = (surplus - buyQuantity) where id = 1 and (surplus - buyQuantity) > 0 ;

    2. 悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续 。

      select … for update

    3. 乐观锁:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新进行重试。

      version 或者 时间戳(CAS思想)。

    悲观锁

    在MySQL的InnoDB中,预设的Tansaction isolation level 为REPEATABLE READ(可重读)
    在SELECT 的读取锁定主要分为两种方式:

    • SELECT … LOCK IN SHARE MODE (共享锁)
    • SELECT … FOR UPDATE (悲观锁)
    • 这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。
    • 而主要的不同在于LOCK IN SHARE MODE 在有一方事务要Update 同一个表单时很容易造成死锁。
    • 简单的说,如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT … FOR UPDATE。

    代码实现

    改造StockService:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    在StockeMapper中定义selectStockForUpdate方法:

    public interface StockMapper extends BaseMapper<Stock> {
    
        public Stock selectStockForUpdate(Long id);
    }
    
    • 1
    • 2
    • 3
    • 4

    在StockMapper.xml中定义对应的配置:

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.atguigu.distributedlock.mapper.StockMapper">
    
        <select id="selectStockForUpdate" resultType="com.atguigu.distributedlock.pojo.Stock">
            select * from db_stock where id = #{id} for update
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    压力测试

    注意:测试之前,需要把库存量改成5000。压测数据如下:比jvm性能高很多,比无锁要低将近1倍
    在这里插入图片描述

    mysql数据库存:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    乐观锁

    乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则重试。那么我们如何实现乐观锁呢

    使用数据版本(Version)记录机制实现,这是乐观锁最常用的实现 方式。一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录 的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新。

    给db_stock表添加version字段:

    在这里插入图片描述

    对应也需要给Stock实体类添加version属性。此处略。。。。

    代码实现

    public void checkAndLock() {
    
        // 先查询库存是否充足
        Stock stock = this.stockMapper.selectById(1L);
    
        // 再减库存
        if (stock != null && stock.getCount() > 0){
            // 获取版本号
            Long version = stock.getVersion();
    
            stock.setCount(stock.getCount() - 1);
            // 每次更新 版本号 + 1
            stock.setVersion(stock.getVersion() + 1);
            // 更新之前先判断是否是之前查询的那个版本,如果不是重试
            if (this.stockMapper.update(stock, new UpdateWrapper<Stock>().eq("id", stock.getId()).eq("version", version)) == 0) {
                checkAndLock();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    重启后使用jmeter压力测试工具结果如下:

    在这里插入图片描述

    修改测试参数如下:

    在这里插入图片描述

    测试结果如下:

    在这里插入图片描述

    说明乐观锁在并发量越大的情况下,性能越低(因为需要大量的重试);并发量越小,性能越高。

    mysql锁总结

    性能:一个sql > 悲观锁 > jvm锁 > 乐观锁

    如果追求极致性能、业务场景简单并且不需要记录数据前后变化的情况下。

    ​ 优先选择:一个sql

    如果写并发量较低(多读),争抢不是很激烈的情况下优先选择:乐观锁

    如果写并发量较高,一般会经常冲突,此时选择乐观锁的话,会导致业务代码不间断的重试。

    ​ 优先选择:mysql悲观锁

    不推荐jvm本地锁。

  • 相关阅读:
    ubuntu20.04 编译内核源码5.15.58
    Codeforces Round #835 (Div. 4) E. Binary Inversions
    【DC-DC】AP9180 内置 MOS 管升压型恒流驱动芯片
    怎样做数字化转型?有没有通用的路径和准则?
    Python学习笔记15:进阶篇(四)文件的读写。
    24/8/9算法笔记 决策树VS线性回归
    Docker容器实现MySQL主从复制
    php调取java类方法,jar包,亲测已上线!windows!
    npm下载包速度慢-淘宝NPM镜像服务器--如何切换其他服务器下载
    command failed: npm install --loglevel error --legacy-peer-deps
  • 原文地址:https://blog.csdn.net/qq_39017153/article/details/133950887