面试系列 - Redis分布式锁3种实现方式对比分析

目录

一、SETNX命令的方式:

二、SETNX实现的分布式锁 

三、Redlock分布式锁


在分布式系统中,实现分布式锁是一个常见的问题,Redis 是一个常用的工具,用于实现分布式锁。

一、SETNX命令的方式:

  • 实现原理: 该方式使用 Redis 的 SETNX 命令(SET if Not eXists)来尝试设置一个锁键。如果该键不存在,它会被设置,并且返回成功。如果键已存在,表示锁已被其他客户端持有,则获取锁失败。

  • 优点: 实现简单,易于理解。适用于基本的锁需求。

  • 缺点: 存在死锁风险,如果持有锁的客户端发生崩溃或其他故障,锁可能会一直被占用。没有续约机制,可能导致锁超时。适用于单节点 Redis 环境。

SETNX 是 Redis 中的一个命令,用于设置一个键的值,但仅当该键不存在时。你可以使用 SETNX 命令来实现基本的分布式锁。

返回值:

  • 1 设置key成功

  • 0 设置key失败

以下是使用 Java 和 Jedis 客户端,演示如何使用 SETNX 来实现分布式锁:

import redis.clients.jedis.Jedis;

public class DistributedLock {

    private static final String LOCK_KEY = "mylock";
    private static final int LOCK_EXPIRE = 10000; // 锁的过期时间(毫秒)
    private static final int ACQUIRE_TIMEOUT = 2000; // 获取锁的超时时间(毫秒)

    public static boolean acquireLock(Jedis jedis, String lockValue) {
        long startTime = System.currentTimeMillis();
        try {
            while (System.currentTimeMillis() - startTime < ACQUIRE_TIMEOUT) {
                Long result = jedis.setnx(LOCK_KEY, lockValue); // 尝试获取锁
                if (result == 1) {
                    // 锁获取成功
                    jedis.pexpire(LOCK_KEY, LOCK_EXPIRE); // 设置锁的过期时间
                    return true;
                } else {
                    // 锁获取失败,等待一段时间后重试
                    Thread.sleep(50); // 等待一段时间后重试
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false; // 获取锁失败
    }

    public static void releaseLock(Jedis jedis, String lockValue) {
        try {
            String currentValue = jedis.get(LOCK_KEY);
            if (currentValue != null && currentValue.equals(lockValue)) {
                jedis.del(LOCK_KEY); // 释放锁
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String lockValue = "unique-lock-value";

        if (acquireLock(jedis, lockValue)) {
            // 获取锁成功,执行业务逻辑
            System.out.println("Lock acquired. Executing business logic...");

            // 模拟业务逻辑执行
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            releaseLock(jedis, lockValue); // 释放锁
            System.out.println("Lock released.");
        } else {
            System.out.println("Failed to acquire lock.");
        }

        jedis.close();
    }
}

在上面的示例中,acquireLock 方法尝试使用 SETNX 命令来获取锁,如果获取成功,它将设置锁的过期时间,然后返回 true。如果获取锁失败,它会等待一段时间后重试,直到达到获取锁的超时时间为止。

releaseLock 方法用于释放锁,它会首先检查当前锁是否属于当前客户端,然后删除锁。

二、SETNX实现的分布式锁 

  • 实现原理: 与 SETNX 不同,该方式在设置锁键时加入了过期时间。通过 SET 命令设置锁键,并为其设置一个过期时间。如果持有锁的客户端在指定时间内未续约锁,锁会自动过期释放。

  • 优点: 有自动过期机制,降低死锁风险。适用于单节点 Redis 环境。

  • 缺点: 仍然存在死锁风险,例如客户端在获取锁后崩溃,未能续约锁,锁可能会过早释放。不适用于多节点 Redis 环境。

以下是使用 Java 和 Jedis 客户端来实现基于 SETNX 的分布式锁的示例:

import redis.clients.jedis.Jedis;

public class DistributedLock {
    private static final String LOCK_KEY = "mylock";
    private static final String LOCK_VALUE = "locked";
    private static final int LOCK_EXPIRE_TIME = 10000; // 锁的过期时间,单位毫秒

    public static boolean acquireLock(Jedis jedis) {
        // 使用 SETNX 命令尝试获取锁
        Long result = jedis.setnx(LOCK_KEY, LOCK_VALUE);
        if (result == 1) {
            // 获取锁成功,设置锁的过期时间
            jedis.pexpire(LOCK_KEY, LOCK_EXPIRE_TIME);
            return true;
        }
        // 获取锁失败
        return false;
    }

    public static void releaseLock(Jedis jedis) {
        // 释放锁
        jedis.del(LOCK_KEY);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        try {
            if (acquireLock(jedis)) {
                // 获取锁成功,执行业务逻辑
                System.out.println("Lock acquired. Executing business logic...");
                Thread.sleep(5000); // 模拟业务逻辑执行
            } else {
                System.out.println("Failed to acquire lock.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            releaseLock(jedis);
            System.out.println("Lock released.");
            jedis.close();
        }
    }
}

在这个示例中,acquireLock 方法使用 setnx 命令尝试获取锁,如果返回值是 1,表示获取锁成功,然后设置锁的过期时间(通过 pexpire 命令)。releaseLock 方法用于释放锁,它使用 del 命令来删除锁键。

三、Redlock分布式锁

Redlock 是一个用于实现分布式锁的算法,由 Redis 的作者 Antirez 在一篇博客中提出。它的目标是提供高可用性和分布式环境下的锁定机制,以防止多个客户端同时访问共享资源。Redlock 是一种基于多个 Redis 节点的互斥锁算法。

  • 实现原理:

  1. 多个 Redis 节点: Redlock 需要至少 N 个 Redis 节点来工作。这些节点可以分布在不同的机器上,以确保高可用性。

  2. 锁的获取: 客户端尝试在 N 个 Redis 节点上获取锁,使用 SETNX 命令(SET if Not eXists)。

  3. 设置过期时间: 为了避免死锁,锁必须设置一个过期时间,确保锁在一段时间后自动释放。

  4. 成功获取锁: 如果客户端成功在至少 N/2 + 1 个节点上获取锁,并且锁设置了相同的唯一标识,那么锁被视为成功获取。

  5. 续约锁: 客户端可以定期续约锁,即在锁过期前延长锁的过期时间。这有助于防止锁过早释放。

  6. 释放锁: 客户端可以释放锁,通过删除在至少 N/2 + 1 个节点上设置的锁

  • 优点: 提供更高的锁可用性,适用于多节点 Redis 环境。具备容错性,可以应对 Redis 节点故障。

  • 缺点: 实现相对复杂,引入了网络通信的延迟。需要精心配置和调整以确保性能和可用性。可能需要较长的锁定和续约时间,导致锁等待时间较长。

简单的 Java 示例,演示如何使用 Jedis 客户端库来实现 Redlock 分布式锁:

import redis.clients.jedis.Jedis;

public class RedlockDemo {
    private static final String LOCK_KEY = "mylock";
    private static final int LOCK_EXPIRE_TIME = 10000; // 锁的过期时间,单位毫秒

    public static void main(String[] args) {
        Jedis jedis1 = new Jedis("redis-node-1", 6379);
        Jedis jedis2 = new Jedis("redis-node-2", 6379);
        Jedis jedis3 = new Jedis("redis-node-3", 6379);

        Redlock redlock = new Redlock(jedis1, jedis2, jedis3);

        try {
            Redlock.Lock lock = redlock.lock(LOCK_KEY, LOCK_EXPIRE_TIME);
            if (lock != null) {
                // 获取锁成功,执行业务逻辑
                System.out.println("Lock acquired. Executing business logic...");
                Thread.sleep(5000); // 模拟业务逻辑执行
            } else {
                System.out.println("Failed to acquire lock.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redlock.unlock(LOCK_KEY);
            System.out.println("Lock released.");
            jedis1.close();
            jedis2.close();
            jedis3.close();
        }
    }
}

这个示例中,我们使用 Jedis 客户端创建了三个 Redis 节点,并使用 Redlock 来获取和释放分布式锁。请注意,Redlock 是一个相对复杂的算法,实际使用中需要更多的容错和错误处理机制。此外,确保你的 Redis 节点是高可用的,以减少故障的影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值