Redis锁经常都会使用到,下面给大家详解3种常见的Redis锁实现@mikechen
1.SETNX实现锁
基于SETNX命令的简单锁,使用SETNX命令可以设置一个键的值,只有当键不存在时才会设置成功。
SETNX是”SET if Not eXists”的缩写,命令格式:
- SETNX key value
参数说明:
key
:要设置的键名。value
:要设置的值。
返回值:
- 当键被成功设置时,返回1。
- 当键已经存在,无法设置时,返回0。
获取锁:
- SETNX lock_key unique_identifier
lock_key
是用作锁的键名。unique_identifier
是一个唯一标识符,可以是客户端ID或者其他具有唯一性的值。
如果SETNX命令执行成功(返回1),表示锁获取成功,可以执行对应的业务逻辑。如果SETNX命令返回0,表示锁已被其他客户端持有,获取锁失败。
释放锁:
- DEL lock_key
使用DEL命令删除锁的键名,释放锁资源。
需要注意的是,在获取锁后,执行业务逻辑时应设定合理的超时时间,以避免锁被长时间占用。
这种方式实现的锁存在一定的缺陷,当 Redis 服务器故障或者出现网络分区时,可能会导致锁无法正常释放,从而导致死锁的问题。
2.Set实现锁
为了解决死锁的问题,我们可以使用 SET 命令结合过期时间来实现分布式锁,当一个线程需要获取锁时,它可以执行如下 Redis 命令:
- SET lock_key value EX expire_time NX
其中:
lock_key :是锁的名称;
value :是锁的值;
expire_time:是锁的过期时间,NX 表示只有在锁不存在的情况下才设置锁。
如果命令执行成功,说明当前线程成功获取到了锁,可以执行相关操作,否则说明锁已经被其他线程占用,当前线程需要等待一段时间后再次尝试获取锁。
示例:
- import redis.clients.jedis.Jedis;
- public class RedisSetLockExample {
- private static final int LOCK_EXPIRY_TIME = 10; // 锁的过期时间,单位为秒
- private Jedis jedis;
- private String lockKey;
- private String lockValue;
- public RedisSetLockExample() {
- // 创建Redis连接
- jedis = new Jedis("localhost", 6379);
- lockKey = "mylock";
- lockValue = "locked";
- }
- public boolean acquireLock() {
- // 使用SET命令设置键值对,并设置锁的过期时间
- String result = jedis.set(lockKey, lockValue, "NX", "EX", LOCK_EXPIRY_TIME);
- return "OK".equals(result);
- }
- public void releaseLock() {
- // 使用DEL命令删除锁的键
- jedis.del(lockKey);
- }
- public static void main(String[] args) {
- RedisSetLockExample redisSetLockExample = new RedisSetLockExample();
- if (redisSetLockExample.acquireLock()) {
- try {
- // 锁获取成功,执行业务逻辑
- System.out.println("锁获取成功");
- // 执行业务逻辑代码
- // ...
- } finally {
- // 释放锁
- redisSetLockExample.releaseLock();
- System.out.println("锁已释放");
- }
- } else {
- System.out.println("锁获取失败");
- }
- // 关闭Redis连接
- redisSetLockExample.jedis.close();
- }
- }
3.Redlock实现锁
Redlock锁它是由Redis的作者Salvatore Sanfilippo提出的,旨在提供一个可靠的分布式锁方案。
以下是Redlock算法的基本步骤:
- 获取当前时间戳:所有Redis实例使用相同的时间源,例如:NTP获取当前时间戳。
- 尝试在多个Redis实例上获取锁:在每个Redis实例上尝试使用SET命令获取锁,设置一个带有唯一标识符的键,设置的键名应该是全局唯一的,以避免与其他锁冲突。
- 计算获取锁所花费的时间:计算从第一步获取时间戳到成功获取锁所花费的时间,记为
elapsed_time
。 - 判断锁是否获取成功:如果获取锁的时间
elapsed_time
小于设定的锁超时时间,并且大多数(例如大于一半)的Redis实例成功获取了锁,那么认为锁获取成功。 - 释放锁:在所有成功获取锁的Redis实例上执行释放锁的操作,使用DEL命令删除对应的键。
示例:
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
- import java.util.ArrayList;
- import java.util.List;
- public class RedlockExample {
- private static final int LOCK_EXPIRY_TIME = 30000; // 锁的过期时间,单位为毫秒
- private static final int LOCK_WAIT_TIME = 1000; // 获取锁的等待时间,单位为毫秒
- private static final int RETRY_COUNT = 3; // 获取锁的重试次数
- private List<JedisPool> jedisPools;
- public RedlockExample() {
- // 初始化Redis连接池
- jedisPools = new ArrayList<>();
- jedisPools.add(createJedisPool("localhost", 6379));
- jedisPools.add(createJedisPool("localhost", 6380));
- jedisPools.add(createJedisPool("localhost", 6381));
- }
- private JedisPool createJedisPool(String host, int port) {
- JedisPoolConfig config = new JedisPoolConfig();
- return new JedisPool(config, host, port);
- }
- public boolean acquireLock(String lockKey, String uniqueIdentifier) throws InterruptedException {
- int acquiredCount = 0;
- for (int i = 0; i < RETRY_COUNT; i++) {
- long currentTime = System.currentTimeMillis();
- boolean lockAcquired = true;
- for (JedisPool jedisPool : jedisPools) {
- try (Jedis jedis = jedisPool.getResource()) {
- // 尝试在当前Redis节点上获取锁
- String result = jedis.set(lockKey, uniqueIdentifier, "NX", "PX", LOCK_EXPIRY_TIME);
- if (result == null) {
- lockAcquired = false;
- break;
- }
- }
- }
- if (lockAcquired) {
- acquiredCount++;
- }
- long elapsedTime = System.currentTimeMillis() - currentTime;
- // 判断是否成功获取锁
- if (acquiredCount >= (jedisPools.size() / 2) + 1 && elapsedTime < LOCK_EXPIRY_TIME) {
- return true;
- }
- // 等待一段时间后重试
- Thread.sleep(LOCK_WAIT_TIME);
- }
- return false;
- }
- public void releaseLock(String lockKey) {
- for (JedisPool jedisPool : jedisPools) {
- try (Jedis jedis = jedisPool.getResource()) {
- jedis.del(lockKey);
- }
- }
- }
- public static void main(String[] args) {
- RedlockExample redlockExample = new RedlockExample();
- String lockKey = "mylock";
- String uniqueIdentifier = "client001";
- try {
- if (redlockExample.acquireLock(lockKey, uniqueIdentifier)) {
- System.out.println("锁获取成功");
- // 执行业务逻辑
- // ...
- } else {
- System.out.println("锁获取失败");
- }
- } catch (InterruptedException e) {
- System.err.println("获取锁时发生中断异常:" + e.getMessage());
- } finally {
- redlockExample.releaseLock(lockKey);
- System.out.println("锁已释放");
- }
- }
- }