zoukankan      html  css  js  c++  java
  • 高并发写测试悲观锁,乐观锁

    源码地址
    有纰漏,错误,欢迎指正,谢谢

    JMeter测试工具

    • 需要创建一个心的工程;
    • 添加一个线程组—这里面设置秒级并发数;
    • 添加一个请求—这里设置压力测试的接口;参数使用:${ }可以从csv文件中获取数据
    • 请求头管理—添加需要修改的请求头信息;
    • CSV文件—可以将请求的参数,以变量的形式,从csv文件中获取,模拟多种请求数据;

    具体怎么用,百度

    测试场景

    • 测试场景为:请求对应商品,减少对应库存;

    • 并发量:秒级1w请求;

    • 声明:本次测试,请求操作失败,直接丢弃,没有采取补救措施,单纯测试两种锁的写性能;

    不加锁

    Controller就不写了,但是要测试,需要通过Request测试

    Mapper:直接更新stock库存数量

    int updateStock(ProductLock lock);
    
    <update id="updateStock" parameterType="com.example.lock.entity.ProductLock">
        update product_lock
        set stock = stock-#{stock,jdbcType=INTEGER}
    	where id = #{id,jdbcType=INTEGER}
        AND
        stock > 0
    </update>
    

    Service:先查询到商品详情,判断库存是否充足,更新库存数量

    @Transactional(rollbackFor = Exception.class)
    public int updateStock(ProductLockDto dto) throws Exception{
        int res=0;
        ProductLock entity=productLockMapper.selectByPrimaryKey(dto.getId());
        // 判断:要更新的库存 > 当前库存
        if (entity!=null && entity.getStock().compareTo(dto.getStock())>=0){
            entity.setStock(dto.getStock());
            return productLockMapper.updateStock(entity);
        }
        return res;
    }
    

    通过JMeter测试(秒级1w并发量),出现问题:

    1. 库存剩余数 > 应该剩余数,存在少卖现象;

    乐观锁—版本号

    修改表结构:

    1. 添加字段:版本号version

    修改Mapper:

    <update id="updateStock_2" parameterType="com.example.lock.entity.ProductLock">
        update product_lock
        set stock = stock-#{stock,jdbcType=INTEGER},version = version + 1
        where
        id = #{id,jdbcType=INTEGER}
        AND
        version = #{version,jdbcType=INTEGER}
        AND
        stock > 0
    </update>
    

    测试

    依然秒级并发1w请求测试:(1w个请求,每个请求减少库存数=2)

    测试前数据:库存2w,version=0

    测试数据后:库存18298,version=851

    分析

    首先,真正请求通过,并修改了库存的请求数应该等于version的增量;

    即:version * 2 = 减少库存数(因为每个version修改,库存减少2)

    851 * 2 = 1702 也就等于 20000 - 18298 = 1702 说明:后端数据修改是没有问题的;

    但是:version修改次数,可以认为是成功响应请求次数:851个;

    1w请求,响应了851个,不太象话;

    结论

    1. 不存在超卖,少卖的情况;
    2. 高并发下,不能响应所有请求,只能响应少部分请求;
    3. 因为乐观锁的机制,version字段,判断失败,直接不做操作,导致大部分请求,无法成功;

    在高并发写入的情况下,不应该使用乐观锁!,后面悲观锁效果更好

    悲观锁—for update

    添加Mapper:

    <!--悲观锁实现 for update 锁表-->
    <select id="selectByIdNegative" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from product_lock
        where id = #{id}
        for update
    </select>
    <update id="updateStock_3" parameterType="com.example.lock.entity.ProductLock">
        update product_lock
        set stock = stock-#{stock,jdbcType=INTEGER},version = version+1
        where
        id = #{id,jdbcType=INTEGER}
        AND
        stock > 0
    </update>
    

    Service层:@Transactional注解,保证上面两个方法,是一个事务,才能实现for update 锁表

    @Transactional(rollbackFor = Exception.class)
    public int updateStock_3(ProductLockDto dto){
        int res =0;
        // select-for update 查询
        ProductLock entity = productLockMapper.selectByIdNegative(dto.getId());
        // 判断version字段,库存是否充足
        if (entity!=null && entity.getStock().compareTo(dto.getStock())>=0){
            entity.setStock(dto.getStock());
            res = productLockMapper.updateStock_3(entity);
        }
        if (res > 0) {
            log.info("减少库存=>{}",dto.getStock());
        }
        return res;
    }
    

    测试

    依然秒级并发1w请求测试:(1w个请求,每个请求减少库存数=2)

    测试前数据:库存2w,version=0

    测试数据后:库存356,version=9822

    分析

    与之前一样:version*2 = 减少库存数(因为每个version修改,库存减少2)

    9822 * 2 = 19644 也就等于 20000 - 356 = 19644 说明:后端数据修改是没有问题的;

    同样:并没有响应所有的请求,但是从version的修改次数看,

    1w请求,响应了9822个请求失败178个

    结论

    1. 不存在超卖,少卖的情况;
    2. 高并发下,不能响应所有请求,但是响应绝大部分请求;
    3. 在写方面,优于乐观锁;
  • 相关阅读:
    [LeetCode 220.] 存在重复元素 III
    C++ 构造函数 & 析构函数
    [LeetCode 891.] 子序列宽度之和【hard】
    [LeetCode 447.] Number of Boomerangs
    HJ93 数组分组
    HJ77 火车进站
    [LeetCode 338.] 比特位计数
    线段树
    大数量问题的一般解决方法
    字典树
  • 原文地址:https://www.cnblogs.com/mussessein/p/12078074.html
Copyright © 2011-2022 走看看