Redis 分布式锁

一、简介

在分布式集群、多实例部署场景下,多台服务节点会同时争抢同一共享资源,容易出现并发数据错乱、商品超卖、定时任务重复执行等问题。

Redis 分布式锁借助 Redis 高性能、单线程串行执行的特性,实现跨机器互斥锁,保证同一时刻只有一个线程执行业务逻辑。生产环境主流使用 Redisson 框架,内置看门狗、防误删、可重入等能力,稳定性更强。

二、适用业务场景

  1. 电商秒杀、商品库存扣减,防止商品超卖
  2. 集群环境定时任务,避免多台机器重复执行
  3. 订单状态变更、金额核算等强一致性并发操作
  4. 第三方接口调用,做接口幂等防重复请求

三、核心原理

  1. 多个服务同时尝试向 Redis 设置同一个唯一 Key
  2. Key 设置成功 = 获取到锁,正常执行业务;
  3. Key 设置失败 = 抢锁失败,直接返回提示或重试;
  4. 业务执行完毕,主动删除 Key 释放锁;
  5. 配置锁超时时间,服务宕机也能自动解锁,彻底避免死锁。

四、Maven 依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.17.0</version>
</dependency>

五、完整代码实战(库存扣减案例)

1. 业务层代码

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class StockService {

    // 注入 Redisson 客户端
    @Resource
    private RedissonClient redissonClient;

    // 库存数据层
    @Resource
    private StockMapper stockMapper;

    /**
     * 分布式锁实现库存扣减
     * @param goodsId 商品ID
     * @param num 扣减数量
     */
    public void deductStock(Long goodsId, Integer num) {
        // 定义锁Key:按商品维度加锁,锁粒度越小,并发性能越好
        String lockKey = "lock:goods:stock:" + goodsId;
        RLock lock = redissonClient.getLock(lockKey);

        boolean isLock = false;
        try {
            // 尝试获取锁:最多等待3秒,锁默认持有10秒
            isLock = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (!isLock) {
                System.out.println("系统繁忙,商品操作中,请稍后重试");
                return;
            }
            // 加锁成功,执行核心业务:扣减库存
            stockMapper.reduceStock(goodsId, num);
            System.out.println("库存扣减成功");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁:校验当前线程是否持有锁,防止误删除其他线程的锁
            if (isLock && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

2. Mapper 数据层

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

public interface StockMapper {

    /**
     * 扣减库存 SQL
     */
    @Update("UPDATE stock SET stock = stock - #{num} WHERE goods_id = #{goodsId} AND stock >= #{num}")
    int reduceStock(@Param("goodsId") Long goodsId, @Param("num") Integer num);
}

六、关键注意事项

  1. 控制锁粒度:优先使用商品ID、订单ID等唯一维度加锁,禁止全局加锁;
  2. 防止死锁:必须设置锁超时时间,统一在 finally 代码块中释放锁;
  3. 防止误删锁:通过 isHeldByCurrentThread() 校验锁归属线程;
  4. 看门狗机制:Redisson 自带自动续期,避免业务未执行完成锁提前过期。

七、业务场景总结

以电商秒杀为例:活动开启瞬间会产生上万条并发请求,多台服务同时操作同一件商品库存。
通过 Redis 分布式锁,可以保证同一商品同一时间仅被一个请求操作,从根源解决商品超卖问题。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐