基于 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 "创建失败";
   }
相关推荐
IvorySQL38 分钟前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·1 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德1 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫1 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i1 小时前
完全卸载MariaDB
数据库·mariadb
期待のcode1 小时前
Redis的主从复制与集群
运维·服务器·redis
纤纡.2 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn2 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露2 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星2 小时前
sql语言之分组语句group by
java·数据库·sql