当前位置: 首页 > 图灵资讯 > 技术篇> 如何使用 Redis 实现分布式锁?(附教学视频)

如何使用 Redis 实现分布式锁?(附教学视频)

来源:图灵教育
时间:2023-05-29 13:59:44

redis 实现分布式锁

redis 实现分布式锁有两种方法:

通过 redis 提供的 setnx 实现,往 redis 中使用 setnx 插入 key 时,如果 key 如果存在,则返回 可插入0,可插入 key 判断分布式锁的返回值

通过使用 Redission(客户端)实现分布式锁。可以调用 Redission 提供的 api,即 lock(),unlock()释放加锁和锁的方法。此外, Redission 还提供了 watchdog,也就是看门狗,来加锁 key 每隔 10 s对该 key 续时(避免锁过期)

Redission 所有指令都是通过的 lua 实现脚本,lua 脚本可以保证所有指令执行的原子性

关键词:setnx,Redission,lock,unlock,watchdog,lua,原子性

基于 Redis 分布式锁实现思路

下面是使用 Redis 分布式锁的步骤:

生成分布式锁 key,一般与业务相关的业务 key;

生成分布式锁 value,一般为1或线程 id;

生成分布式锁的过期时间,避免锁过程中停机锁无法解开;

通过不同的语言 API 实现 SET key value [EX seconds] [NX]命令;

一般来说,通过命令执行结果返回的标志来判断锁是否成功。 true 成功上锁;

释放锁时,删除分布式锁 key 实现。

Java 实现

这里提供了实现不可重入分布式锁的方法:

@Componentpublic class RedisLock {    @Resource    private StringRedisTemplate redisTemplate;    /**     * 尝试获得分布式锁     * @param key 锁的键值     * @param value 锁的值     * @param expireTime 锁的过期时间     * @param timeUnit 锁的过期时间单位     * @return 是否获得锁     */    public boolean tryLock(String key, String value, long expireTime, TimeUnit timeUnit) {        // 利用 setIfAbsent 实现分布式锁的方法        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);        return Boolean.isTrue(success);    }    /**     * 释放分布式锁     * @param key 锁的键值     */    public void unlock(String key) {        redisTemplate.delete(key);    }}

什么是可重入锁?

可重新进入分布式锁是指在分布式环境下,同一线程可多次获得同一锁,无死锁或其他异常情况。这是因为可重新进入锁将记录当前线程中获得锁的次数,每次释放锁的次数将减少一次,只有当次数为零时,锁才会真正释放。在分布式环境中,当前线程的标志和获取锁的次数可以重新存储在分布式存储系统中,以便其他节点可以识别当前线程是否已获得锁,并正确释放锁。

可重新进入锁的优点是可以避免死锁和其他异常情况,也可以提高代码的可读性和可维护性。但在分布式环境下,需要考虑网络延迟、节点故障等因素,因此需要仔细设计和测试。

可重入锁的简单实现

以下是使用Java和Redis实现可重入分布式锁的示例代码:

import redis.clients.jedis.Jedis;public class RedisReentrantLock {    private Jedis jedis;    private String lockKey;    private String lockValue;    private int lockCount;    public RedisReentrantLock(Jedis jedis, String lockKey) {        this.jedis = jedis;        this.lockKey = lockKey;        this.lockValue = null;        this.lockCount = 0;    }    public synchronized boolean lock() {        if (lockCount > 0) {            lockCount++;            return true;        }        String value = String.valueOf(System.currentTimeMillis());        if (jedis.setnx(lockKey, value) == 1) {            lockValue = value;            lockCount = 1;            return true;        } else {            String currentValue = jedis.get(lockKey);            if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {                String oldValue = jedis.getSet(lockKey, value);                if (oldValue != null && oldValue.equals(currentValue)) {                    lockValue = value;                    lockCount = 1;                    return true;                }            }        }        return false;    }    public synchronized boolean unlock() {        if (lockCount == 0) {            return false;        }        lockCount--;        if (lockCount == 0) {            jedis.del(lockKey);            lockValue = null;        }        return true;    }}

在这个例子中,我们使用Jedis客户端连接Redis服务器。构造函数以Jedis实例和锁的键值为参数。lock()方法用于获取锁。如果当前线程已获得锁,则增加锁的计数器。否则,尝试使用setnx命令在redis中创建一个新的键值对。如果创建成功,则表示当前线程已获得锁,否则尝试获得当前锁的值并比较是否过期。如果过期,则使用getset命令更新锁的值。如果更新成功,则表示当前线程已获得锁。unlock()释放锁的方法,如果当前线程仍持有锁,则减少锁的计数器,如果计数器为零,则删除锁的键值对。

需要注意的是,这个例子中的锁并不是完全可重新进入的,因为它只记录了锁的计数器,而不记录获取锁的线程。如果多个线程使用相同的锁键值,并试图同时获得锁,则可能会出现死锁或其他异常情况。为了实现完全可重新进入的锁,需要在锁值中记录获取锁的线程标志,并在释放锁时检查当前线程是否持有锁。

Redison实现分布式锁

Redison是基于Redis的Java驻留对象服务(Remote Service)和分布式锁框架。它提供了一种简单而强大的方法来实现分布式锁,以确保分布式环境中多个过程或线程之间的相互排斥。

以下是使用Redisson实现分布式锁的步骤:

  1. 依赖Redison引入Redisson

Redisson依赖可以通过以下方式引入Maven项目:

<dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson</artifactId>    <version>3.15.5</version></dependency>

  1. 创建Redison客户端

在使用Redison之前,需要创建Redison客户端。可以通过以下方式创建:

Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);

这里使用单节点模式。如果是集群模式,则需要使用useClusterServers()方法。

  1. 获取分布式锁

获得分布式锁的方法非常简单,只需调用即可getLock()方法即可:

RLock lock = redisson.getLock("myLock");lock.lock();try {    // 执行业务逻辑} finally {    lock.unlock();}

这里创建了一个名字myLock锁,并通过lock()方法获取锁。在执行业务逻辑之前,需要先获取锁,否则会被堵塞。

  1. 释放分布式锁

执行业务逻辑后,需要释放分布式锁,否则其他流程或线程无法获得锁。可以通过unlock()方法释放锁:

lock.unlock();

总结:

使用Redison实现分布式锁非常简单,只需引入依赖、创建Redison客户端、获取锁和释放锁即可。与此同时,Redison还提供了许多其他分布式服务,如分布式Mapp、分布式List等,可以轻松实现分布式应用。

教学视频

我还录制了一套redis分布式锁的视频(https://www.tulingxueyuan.cn/d/file/p/20230529/iax3vvdjaym