当前位置: 首页 > article >正文

7. 基于 Redis 实现分布式锁

在分布式系统中,多个服务或节点同时访问共享资源时,容易引发并发问题。为了防止资源的重复访问或更新,分布式锁应运而生。Redis 是一种高效的分布式锁实现方式,因其高性能和原子性特性而广泛应用于实际项目中。

本文将基于 Redis 和 Spring Boot ,详细介绍如何实现分布式锁,包括实现原理、应用场景、具体过程和效果分析。

一、分布式锁的应用场景

分布式锁的典型应用场景包括:

  1. 库存扣减:在电商系统中,多个用户同时下单时,可能同时访问库存系统,若没有锁,容易导致超卖。
  2. 任务调度:在分布式任务调度系统中,多个任务实例可能会同时启动,分布式锁可以确保同一时刻只有一个任务实例执行某个操作。
  3. 资源竞争:在多个节点或服务同时操作同一资源(如数据库记录、文件等)时,使用分布式锁可以避免资源冲突。
二、Redis 分布式锁的实现原理

Redis 实现分布式锁的核心原理是通过原子性操作来确保同一时刻只有一个客户端能够成功获取锁。Redis 提供的 SET 命令支持 NX(不存在时设置)和 PX(设置过期时间)选项,通过这两个选项,可以在 Redis 中实现分布式锁的基本功能。

Redis 分布式锁的关键点如下:

  1. 获取锁:使用 SET key value NX PX expire_time 原子性操作。如果指定的 key 不存在,Redis 会设置该 key 并返回成功,表示获取到锁。否则返回失败,表示锁已被其他客户端持有。
  2. 释放锁:在操作完成后,需要释放锁。释放锁的关键在于,只有锁的持有者才可以释放锁,这可以通过判断 value 是否与当前客户端匹配来实现。
  3. 超时机制:为防止锁长时间占用,锁设置了过期时间,即使客户端未主动释放锁,锁也会在超时后自动释放。
三、使用 Redis 实现分布式锁的步骤

接下来,通过 Spring Boot 结合 Redis 来实现分布式锁。

1. 项目配置

首先,创建一个 Spring Boot 项目,并引入 Redis 相关的依赖。

<dependencies>
    <!-- Spring Boot Starter for Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

application.properties 中配置 Redis 连接:

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourpassword
2. 实现 Redis 分布式锁工具类

为了简化锁的使用,我们可以创建一个 RedisLockUtil 工具类,封装获取锁和释放锁的逻辑。

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLockUtil {

    private final StringRedisTemplate redisTemplate;

    public RedisLockUtil(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取分布式锁
     * @param key 锁的标识
     * @param expireTime 锁的过期时间(秒)
     * @return 锁的唯一标识(用于释放锁时校验)
     */
    public String tryLock(String key, long expireTime) {
        String lockId = UUID.randomUUID().toString();
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, lockId, expireTime, TimeUnit.SECONDS);
        return success != null && success ? lockId : null;
    }

    /**
     * 释放分布式锁
     * @param key 锁的标识
     * @param lockId 锁的唯一标识
     */
    public void releaseLock(String key, String lockId) {
        String currentValue = redisTemplate.opsForValue().get(key);
        if (lockId.equals(currentValue)) {
            redisTemplate.delete(key);
        }
    }
}
3. 使用 RedisLockUtil 实现分布式锁

在实际应用中,我们可以在需要同步的操作上使用分布式锁,确保操作的唯一性。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StockController {

    @Autowired
    private RedisLockUtil redisLockUtil;

    private static final String LOCK_KEY = "stock_lock";

    @GetMapping("/deductStock")
    public String deductStock() {
        String lockId = redisLockUtil.tryLock(LOCK_KEY, 10);
        if (lockId == null) {
            return "系统繁忙,请稍后再试";
        }

        try {
            // 模拟扣减库存的操作
            Thread.sleep(1000); // 假设扣减库存需要 1 秒
            System.out.println("库存扣减成功");
            return "库存扣减成功";
        } catch (InterruptedException e) {
            return "操作失败";
        } finally {
            redisLockUtil.releaseLock(LOCK_KEY, lockId);
        }
    }
}
4. 测试效果

启动 Spring Boot 应用,访问 /deductStock 接口进行库存扣减操作。由于我们使用了 Redis 分布式锁,在并发环境下同一时刻只能有一个请求成功获取到锁并执行扣减库存的操作,其余请求将被阻塞或返回 “系统繁忙” 提示。

你可以通过 JMeter 等工具模拟高并发请求,观察是否有同时操作的情况发生。

四、分布式锁的效果分析
  1. 高效性:Redis 具有极高的吞吐量和低延迟,能在毫秒级内完成锁的获取和释放,适合高并发场景。
  2. 可靠性:通过锁的唯一标识和自动过期机制,能够防止死锁和锁的误释放。即使在系统异常情况下,锁也会在过期后自动释放,保证系统不会被锁死。
  3. 扩展性:由于 Redis 天生支持分布式部署,Redis 分布式锁可以轻松应用于分布式集群中,具有良好的扩展性。
五、其他优化和改进建议
  1. 锁续期:在某些复杂操作中,锁的持有时间可能会超过设置的过期时间。为避免锁被自动释放,可以实现锁的自动续期功能,即在持有锁期间定期延长锁的过期时间。

  2. Redisson 实现:如果需要更加健壮和全面的分布式锁解决方案,可以使用 Redisson 库,Redisson 提供了更加完善的分布式锁机制,包括自动续期、可重入锁、读写锁等功能。

  3. 锁竞争策略:在高并发场景下,如果大量请求同时竞争锁,可以引入自旋锁或阻塞锁,减少请求获取锁失败后的重试次数,优化系统性能。

Redis 分布式锁为解决分布式系统中的资源竞争问题提供了一种高效、可靠的方案。在实际项目中,合理使用分布式锁可以确保系统的并发安全性。


http://www.kler.cn/a/392346.html

相关文章:

  • DataWorks on EMR StarRocks,打造标准湖仓新范式
  • 限流算法(令牌通漏桶计数器)
  • 【vue2.0入门】vue基本语法
  • 图片画廊 day2 (可复制源码)
  • 算法演练----24点游戏
  • NUXT3学习日记一(在我git中拉取代码、文件讲解)
  • 基于Java Web的传智播客crm企业管理系统的设计与实现
  • 公开仓库改私有再配置公钥后Git拉取仍需要输入用户名的问题
  • 普通用户切换到 root 用户不需要输入密码配置(Ubuntu20)
  • vxe-table 3.10+ 进阶高级用法(一),根据业务需求自定义实现筛选功能
  • 【软考】系统架构设计师-计算机系统基础(2):操作系统
  • 【Linux】Linux 命令awk和sed的简单介绍
  • Vestar:AI造神邸,颠覆Meme叙事的新范式
  • 45.第二阶段x86游戏实战2-hook监控实时抓取游戏lua
  • 【python GUI编码入门-24】使用Tkinter构建一个简单的音乐播放器
  • 【Linux:IO多路复用(select函数)
  • 新的服务器Centos7.6 安装基础的环境配置(新服务器可直接粘贴使用配置)
  • 数据集标注txt文件读取小工具
  • # 如何查看 Ubuntu 版本?
  • Java | Leetcode Java题解之第559题N叉树的最大深度
  • 漏洞分析 | Spring Framework路径遍历漏洞(CVE-2024-38816)
  • 小波神经网络:结合小波变换与神经网络的力量(附Pytorch代码实现)
  • 详细介绍MySQL、Mongo、Redis等数据库的索引
  • Prometheus常用查询PromQL表达式
  • 国家网络安全法律法规
  • mqtt学习笔记(一)