实现基于Redis的分布式锁机制

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,经济版 1GB 1个月
简介: 实现基于Redis的分布式锁机制

实现基于Redis的分布式锁机制

今天我们来讨论一下如何实现基于Redis的分布式锁机制。分布式锁在分布式系统中非常重要,它可以防止多个进程或线程同时访问共享资源,确保数据的一致性和完整性。

1. 为什么选择Redis实现分布式锁

Redis是一种高性能的键值存储系统,支持丰富的操作,具有高并发处理能力。使用Redis实现分布式锁,有以下优点:

  • 性能高效:Redis的读写性能非常高,适合实现高性能的分布式锁。
  • 原子操作:Redis提供了多种原子操作,能够确保锁的操作具有原子性。
  • 持久化:Redis支持数据持久化,能在系统故障后恢复锁的信息。

2. 基于Redis的分布式锁实现思路

基于Redis的分布式锁实现思路如下:

  1. 获取锁:使用SET命令尝试设置一个键,并使用NX选项确保键不存在时才设置。
  2. 释放锁:通过删除锁对应的键来释放锁。
  3. 锁超时:设置键的过期时间,防止死锁。

3. 使用Jedis实现Redis分布式锁

Jedis是一个简单易用的Redis客户端,下面我们通过Jedis实现一个简单的分布式锁。

3.1 引入依赖

pom.xml中添加Jedis的依赖:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.0.0</version>
</dependency>

3.2 实现分布式锁

package cn.juwatech.lock;
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
    private static final String LOCK_KEY = "distributed_lock";
    private static final int EXPIRE_TIME = 30000; // 锁过期时间,单位毫秒
    private Jedis jedis;
    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }
    public boolean acquireLock(String requestId) {
        String result = jedis.set(LOCK_KEY, requestId, "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }
    public boolean releaseLock(String requestId) {
        if (requestId.equals(jedis.get(LOCK_KEY))) {
            jedis.del(LOCK_KEY);
            return true;
        }
        return false;
    }
}

3.3 测试分布式锁

package cn.juwatech.lock;
import redis.clients.jedis.Jedis;
public class LockTest {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        RedisDistributedLock lock = new RedisDistributedLock(jedis);
        String requestId = "unique_request_id";
        
        if (lock.acquireLock(requestId)) {
            System.out.println("获取锁成功");
            // 执行业务逻辑
            if (lock.releaseLock(requestId)) {
                System.out.println("释放锁成功");
            } else {
                System.out.println("释放锁失败");
            }
        } else {
            System.out.println("获取锁失败");
        }
        jedis.close();
    }
}

4. 优化分布式锁

4.1 防止锁误删

在上面的实现中,锁的释放是通过直接删除键来实现的,但这可能导致误删的情况。可以使用Lua脚本保证锁的释放操作是原子的。

package cn.juwatech.lock;
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
    private static final String LOCK_KEY = "distributed_lock";
    private static final int EXPIRE_TIME = 30000; // 锁过期时间,单位毫秒
    private static final String RELEASE_LOCK_SCRIPT =
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else " +
            "return 0 " +
            "end";
    private Jedis jedis;
    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }
    public boolean acquireLock(String requestId) {
        String result = jedis.set(LOCK_KEY, requestId, "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }
    public boolean releaseLock(String requestId) {
        Object result = jedis.eval(RELEASE_LOCK_SCRIPT, 1, LOCK_KEY, requestId);
        return 1L == result;
    }
}

4.2 续租机制

为了防止锁的持有者因为某些原因未能及时释放锁,可以实现续租机制,即在锁快过期时,持有者可以续租锁,延长锁的有效期。

package cn.juwatech.lock;
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
    private static final String LOCK_KEY = "distributed_lock";
    private static final int EXPIRE_TIME = 30000; // 锁过期时间,单位毫秒
    private static final String RENEW_LOCK_SCRIPT =
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('pexpire', KEYS[1], ARGV[2]) " +
            "else " +
            "return 0 " +
            "end";
    private Jedis jedis;
    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }
    public boolean acquireLock(String requestId) {
        String result = jedis.set(LOCK_KEY, requestId, "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }
    public boolean releaseLock(String requestId) {
        Object result = jedis.eval(RELEASE_LOCK_SCRIPT, 1, LOCK_KEY, requestId);
        return 1L == result;
    }
    public boolean renewLock(String requestId) {
        Object result = jedis.eval(RENEW_LOCK_SCRIPT, 1, LOCK_KEY, requestId, String.valueOf(EXPIRE_TIME));
        return 1L == result;
    }
}

5. 集成Spring Boot

在实际项目中,可以将上述代码集成到Spring Boot项目中,通过Spring管理Jedis实例。

5.1 配置Redis

application.properties中配置Redis连接信息:

spring.redis.host=localhost
spring.redis.port=6379

5.2 配置类

package cn.juwatech.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@Configuration
public class RedisConfig {
    @Bean
    public Jedis jedis() {
        return new Jedis("localhost", 6379);
    }
}

5.3 使用Redis分布式锁

package cn.juwatech.service;
import cn.juwatech.lock.RedisDistributedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
@Service
public class LockService {
    @Autowired
    private Jedis jedis;
    public void performTaskWithLock() {
        RedisDistributedLock lock = new RedisDistributedLock(jedis);
        String requestId = "unique_request_id";
        
        if (lock.acquireLock(requestId)) {
            try {
                System.out.println("获取锁成功");
                // 执行业务逻辑
            } finally {
                if (lock.releaseLock(requestId)) {
                    System.out.println("释放锁成功");
                } else {
                    System.out.println("释放锁失败");
                }
            }
        } else {
            System.out.println("获取锁失败");
        }
    }
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
17天前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
68 10
|
20天前
|
NoSQL 算法 Java
(十三)全面理解并发编程之分布式架构下Redis、ZK分布式锁的前世今生
本文探讨了从单体架构下的锁机制到分布式架构下的线程安全问题,并详细分析了分布式锁的实现原理和过程。
|
25天前
|
NoSQL Redis 开发工具
Redis性能优化问题之检查 Redis 实例是否启用了透明大页机制,如何解决
Redis性能优化问题之检查 Redis 实例是否启用了透明大页机制,如何解决
|
25天前
|
NoSQL Redis
Redis性能优化问题之禁用内存大页,如何解决
Redis性能优化问题之禁用内存大页,如何解决
|
4天前
|
NoSQL Java Redis
Redis字符串数据类型之INCR命令,通常用于统计网站访问量,文章访问量,实现分布式锁
这篇文章详细解释了Redis的INCR命令,它用于将键的值增加1,通常用于统计网站访问量、文章访问量,以及实现分布式锁,同时提供了Java代码示例和分布式锁的实现思路。
11 0
|
6天前
|
NoSQL Redis
Redis——设置最大内存 | key淘汰机制
Redis——设置最大内存 | key淘汰机制
18 0
|
20天前
|
数据采集 存储 NoSQL
Redis 与 Scrapy:无缝集成的分布式爬虫技术
Redis 与 Scrapy:无缝集成的分布式爬虫技术
|
26天前
|
存储 缓存 NoSQL
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
redis分布式锁、redisson、可重入、主从一致性、WatchDog、Redlock红锁、zookeeper;Redis集群、主从复制,全量同步、增量同步;哨兵,分片集群,Redis为什么这么快,I/O多路复用模型——用户空间和内核空间、阻塞IO、非阻塞IO、IO多路复用,Redis网络模型
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
|
29天前
|
NoSQL Java Redis
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决