基于 Redis 实现的分布式锁

获取锁

互斥:确保只有一个线程获得锁

bash 复制代码
# 添加锁 利用setnx的互斥性
127.0.0.1:6379> setnx lock thread1

释放锁

手动释放锁

超时释放:获取锁时设置一个超时时间

bash 复制代码
#释放锁 删除即可
127.0.0.1:6379> del lock

两步合成一步

bash 复制代码
 help set


  SET key value [EX seconds] [PX milliseconds] [NX|XX]
  summary: Set the string value of a key
  since: 1.0.0
  group: string


127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> set lock k1 ex 5 nx
OK
127.0.0.1:6379> set lock k1 ex 5 nx
nil


分布式锁解决方案_Redis实现的分布式锁

编写创建订单实现类

bash 复制代码
 @Override
  public String createOrderRedis(Integer productId, Integer count) throws Exception {


    log.info("*************** 进入方法 **********");
    String key = "lock:";
    String value = UUID.randomUUID().toString();


    // 获取分布式锁
    Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key+productId, String.valueOf(Thread.currentThread().getId()),30,TimeUnit.SECONDS);
    // 判断是否获取锁成功
    if (!result){
      log.info("我进入了锁");
      return "不允许重复下单";
     }
    try {
      // 1、根据商品id查询商品信息
      Product product = productMapper.selectById(productId);
      // 2、判断商品是否存在
      if (product == null) {
        throw new RuntimeException("购买商品不存在:" + productId + "不存在");
       }
      // 3、校验库存
      if (count > product.getCount()) {
        throw new RuntimeException("商品" + productId + "仅剩" + product.getCount() + "件,无法购买");
       }
      // 4、计算库存
      Integer leftCount = product.getCount() - count;
      // 5、更新库存
      product.setCount(leftCount);
      productMapper.updateById(product);
      // 6、 创建订单
      TOrder order = new TOrder();
      order.setOrderStatus(1);//待处理
      order.setReceiverName("张三");
      order.setReceiverMobile("18587781068");
      order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
      baseMapper.insert(order);


      // 7、 创建订单和商品关系数据
      OrderItem orderItem = new OrderItem();
      orderItem.setOrderId(order.getId());
      orderItem.setProduceId(product.getId());
      orderItem.setPurchasePrice(product.getPrice());
      orderItem.setPurchaseNum(count);
      orderItemMapper.insert(orderItem);
      return order.getId();
     }catch (Exception e){
      e.printStackTrace();
     }finally {
      // 释放锁
      stringRedisTemplate.delete(key+productId);
     }
    return "创建失败";
   }

Redis分布式锁误删除问题

Redis分布式锁误删除问题解决方案

设置超时时间远大于业务执行时间,但是会带来性能问题

删除锁的时候要判断,是不是自己的,如果是再删除

配置锁标识

bash 复制代码
private static final String KEY_PREFIX = "lock:";
  private static final String ID_PREFIX = UUID.randomUUID().toString().replace("-","");

获取锁

bash 复制代码
    //1、获取线程标识
    String threadId = ID_PREFIX + Thread.currentThread().getId();
 // 2、获得锁  setnx  key  value  time  type
    Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+produceId, threadId, 30, TimeUnit.SECONDS);

释放锁

bash 复制代码
// 获取锁标识
      String s = stringRedisTemplate.opsForValue().get(KEY_PREFIX + produceId);
      // 判断标识是否一致
      if (s.equals(threadId)){
        // 释放锁
        stringRedisTemplate.delete(KEY_PREFIX + produceId);
       }

Redis分布式锁不可重入问题


分布式锁解决方案_基于Redisson实现的分布式锁实现

Redisson介绍

Redisson - 是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象,Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端,Jedis、Lettuce 的 API 更侧重对 Reids 数据库的 CRUD(增删改查),而 Redisson API 侧重于分布式开发。

bash 复制代码
<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson-spring-boot-starter</artifactId>
      <version>3.17.2</version>
 </dependency>

编写Redis分布式锁工具类

bash 复制代码
package com.itbaizhan.lock.utils;


import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import java.util.concurrent.TimeUnit;


@Component
@Slf4j
public class DistributedRedisLock {


  @Autowired
  private RedissonClient redissonClient;


  // 加锁
  public Boolean lock(String lockName) {
    if (redissonClient == null) {
      log.info("DistributedRedisLock redissonClient is null");
      return false;
     }


    try {
      RLock lock = redissonClient.getLock(lockName);
      // 锁15秒后自动释放,防止死锁
      lock.lock(15, TimeUnit.SECONDS);


      log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
      // 加锁成功
      return true;
     } catch (Exception e) {
      log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
      return false;
     }
   }


  // 释放锁
  public Boolean unlock(String lockName) {
    if (redissonClient == null) {
      log.info("DistributedRedisLock redissonClient is null");
      return false;
     }


    try {
      RLock lock = redissonClient.getLock(lockName);
      lock.unlock();
      log.info("Thread [{}] DistributedRedisLock unlock [{}] success", Thread.currentThread().getName(), lockName);
      // 释放锁成功
      return true;
     } catch (Exception e) {
      log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
      return false;
     }
   }
}

编写创建订单接口实现

bash 复制代码
 /**
   * Redis锁实现
   *
   * @param productId
   * @param count
   * @return
   * @throws Exception
   */
  @Override
  public String createOrderRedis(Integer productId, Integer count) throws Exception {
    //获取锁对象
    if (distributedRedisLock.lock(String.valueOf(productId))) {
      try {
        // 1、根据商品id查询商品信息
        Product product = productMapper.selectById(productId);
        // 2、判断商品是否存在
        if (product == null) {
          throw new RuntimeException("购买商品不存在:" + productId + "不存在");
         }
        // 3、校验库存
        if (count > product.getCount()) {
          throw new RuntimeException("商品" + productId + "仅剩" + product.getCount() + "件,无法购买");
         }
        // 4、计算库存
        Integer leftCount = product.getCount() - count;
        // 5、更新库存
        product.setCount(leftCount);
        productMapper.updateById(product);
        // 6、 创建订单
        TOrder order = new TOrder();
        order.setOrderStatus(1);//待处理
        order.setReceiverName("张三");
        order.setReceiverMobile("18587781068");
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
        baseMapper.insert(order);
        // 7、 创建订单和商品关系数据
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProduceId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(count);
        orderItemMapper.insert(orderItem);
        return order.getId();
       } catch (Exception e) {
        e.printStackTrace();
       } finally {
        distributedRedisLock.unlock(String.valueOf(productId));
       }
     }
    return "创建失败";
   }
相关推荐
搞不懂语言的程序员2 分钟前
Redis Sentinel如何实现高可用?
数据库·redis·sentinel
wangzhongyudie11 分钟前
SQL实战:06交叉日期打折问题求解
数据库·sql
掘金-我是哪吒37 分钟前
分布式微服务系统架构第129集:redis安装部署文档
redis·分布式·微服务·架构·系统架构
2401_841003981 小时前
mysql高可用
数据库·mysql
2302_809798321 小时前
【JavaWeb】MySQL(准备篇)
数据库·mysql·datagrip
计算机毕设定制辅导-无忧学长2 小时前
RabbitMQ 消息模式实战:从简单队列到复杂路由(一)
分布式·rabbitmq·ruby
阿四啊2 小时前
【Redis实战篇】秒杀优化
数据库·redis·缓存
川石课堂软件测试2 小时前
涨薪技术|0到1学会性能测试第65课-SQL捕获阻塞事件
数据库·sql·功能测试·oracle·性能优化·单元测试·tomcat
darkchink2 小时前
[LevelDB]LevelDB版本管理的黑魔法-为什么能在不锁表的情况下管理数据?
c语言·数据库·c++·oracle·数据库开发·dba·db
卡戎-caryon3 小时前
【MySQL】02.数据库基础
linux·网络·数据库·mysql·存储引擎