散装java 散装java
首页
  • Java基础
  • JVM
  • Java多线程
  • 知识点
  • 案例
  • Redis
  • RabbitMQ
  • Kafka
  • Elasticsearch
  • MySQL
  • Linux
  • Docker
  • Zookeeper
  • Nginx
  • Git
  • JMeter
  • Gradle
  • 常见BUG
  • 常见解决方案
  • 资源
  • 问答
💖支持
Gitee (opens new window)
首页
  • Java基础
  • JVM
  • Java多线程
  • 知识点
  • 案例
  • Redis
  • RabbitMQ
  • Kafka
  • Elasticsearch
  • MySQL
  • Linux
  • Docker
  • Zookeeper
  • Nginx
  • Git
  • JMeter
  • Gradle
  • 常见BUG
  • 常见解决方案
  • 资源
  • 问答
💖支持
Gitee (opens new window)
  • Spring Framework

    • Spring Framework 源码拉取编译技巧
  • Spring知识点

    • Spring 导读
    • Spring 过滤器和拦截器的区别
    • Spring Boot 自动装配原理是如何实现的
  • Spring集成

  • 案例

    • 大文件上传-分片-秒传-断点续传
    • 布隆过滤器使用
    • Spring Boot 集成 Zookeeper 实现分布式锁
    • Spring Boot 集成 Redis 实现分布式锁
    • Spring Boot 集成 MySQL 实现分布式锁
      • 简介
      • 乐观锁
      • 悲观锁
      • 本地测试说明
  • Spring
  • 案例
散装java
2023-01-13
目录

Spring Boot 集成 MySQL 实现分布式锁

提示

本文中的完整代码已上传 Gitee:

https://gitee.com/bulkall/bulk-demo/tree/master/spring-boot-lock/spring-boot-lock-mysql (opens new window)

# 简介

本项目会演示基于 MySQL 实现分布式锁的使用,会以常见的 “超卖”业务去演示

本项目中包含两种类型的锁

  1. 基于 MySQL 实现的乐观锁
  2. 基于 MySQL 实现的悲观锁

# 乐观锁

我认为我操作之前不会有人操作,如果有人操作了,那我再来一次,即为无锁操作

使用代码如下

@SneakyThrows
@Override
public String reduceStockOptimism(Integer id) {
    ProductStock stock = productStockMapper.selectById(id);
    if (stock != null && stock.getStock() > 0) {
        int i = productStockMapper.reduceStockOptimism(id, stock.getVersion());
        // i = 0 表示没有修改成功,说明有别人在你修改之前 已经修改了数据;需要重新再调用下当前方法
        if (i == 0) {
            // Thread.sleep 防止栈溢出
            // Thread.sleep(10);
            this.reduceStockOptimism(id);
        }
    } else {
        throw new RuntimeException("库存不足!");
    }
    return "ok";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

productStockMapper.reduceStockOptimism 的 SQL 如下

<update id="reduceStockOptimism">
    update product_stock
    set stock   = stock - 1,
        version = version + 1
    where id = #{id}
      and version = #{version}
</update>
1
2
3
4
5
6
7

# 悲观锁

查询的时候,不管有没有线程竞争,都在 MySQL层面就加上了锁

使用代码如下

@Override
@Transactional(rollbackFor = Exception.class)
public String reduceStockGloomy(Integer id) {
    // 加锁查询
    ProductStock stock = productStockMapper.selectForLock(id);
    if (stock != null && stock.getStock() > 0) {
        productStockMapper.reduceStock(id);
    } else {
        throw new RuntimeException("库存不足!");
    }
    return "ok";
}
1
2
3
4
5
6
7
8
9
10
11
12

productStockMapper.selectForLock(id) SQL 如下,借助于 for update 实现加锁

<select id="selectForLock" resultType="top.bulk.lock.mysql.entity.ProductStock">
    select *
    from product_stock
    where id = #{id} for
    update
</select>
1
2
3
4
5
6

# 本地测试说明

  1. 压测工具使用 JMeter , 当然也可以用别的,我用的这个, 此处不了解的可以参考 JMeter 相关操作
  2. 本地测试,借助于 IDEA 的 Allow parallel run 功能启动多个相同的服务(模拟线上环境多个副本),注意修改端口(-Dserver.port=8089),操作可以看下图
  3. 使用 Nginx 工具,将启动的多个项目做负载均衡; 此处不了解的可以参考 Nginx 配置负载均衡
  4. 接下来就是使用 JMeter 开启多个线程去压测 Nginx 暴露出来的接口了
上次更新: 2023/01/13, 18:09:17
Spring Boot 集成 Redis 实现分布式锁

← Spring Boot 集成 Redis 实现分布式锁

Theme by Vdoing | Copyright © 2022-2024 散装java | MIT License | 鲁ICP备2022022143号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式