基于 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 "创建失败";
   }
相关推荐
记忆停留w4 小时前
从单体到微服务:Redis 协同 MySQL、Milvus、MinIO 搭建企业级RAG/AI Agent脚手架架构
大数据·人工智能·redis·微服务·ai·架构·milvus
小二·4 小时前
RAG + 向量数据库实战:ChromaDB / Milvus / FAISS 选型与性能横评
数据库·milvus·faiss
矜持的左手5 小时前
电子小白的枕边书:电子学(The Art of Electronics)
数据库·restful
吴声子夜歌5 小时前
Redis 5.x——布隆过滤器
数据库·redis·缓存
蓝天下的守望者5 小时前
svt_apb_if里的宏定义问题
运维·服务器·数据库
上海云盾-小余6 小时前
网站频繁遭遇 SQL 注入溯源与原生漏洞修复全流程总结
数据库·sql
数据库小学妹6 小时前
国家区域医疗中心国产化改造实战:数据库选型、跨院区数据互通与踩坑经验
数据库·国产数据库·数据库选型·医疗信息化·信创数据库·医疗信创
AllData公司负责人7 小时前
数据库同步平台|AIIData数据中台实现OceanBase、达梦数据库、OpenGauss、人大金仓、Hive、TDengine 一键接入Doris
大数据·数据库·hive·mysql·oceanbase·tdengine
2603_954708317 小时前
全维度容错设计,打造微电网安全运行屏障
服务器·网络·数据库·人工智能·分布式·安全