RedLock是一种分布式锁的实现方式,算法思想很好理解:假设有N个相互独立无任何协作的Redis master,在每个master上使用单机Redis的加锁方式加锁。当有N/2+1个节点加锁成功,即获取分布式锁成功;否则在所有实例上解锁。
客户端行为
- 获取当前Unix时间,以毫秒为单位。
- 依次尝试从N个Master实例使用相同的key和随机值获取锁(假设这个key是LOCK_KEY)。当向Redis设置锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试另外一个Redis实例。
- 客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
- 如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。
- 如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功)。
参考:
RedLock算法理解
使用场景
分布式锁说明
一般说的redis分布式锁,其实是针对多个客户端获取锁和释放锁,redis分布式锁需要保证操作的原子性。这种情况下的,一般一个redis集群中就只有1个master,分布式锁的获取只是单机操作(包括redisson的getLock)。
而如果是cluster集群模式,不同key分布在不同的master上;或者在哨兵模式集群之上加一次负载均衡,串联多个哨兵集群作为一个大集群。针对这种相互独立的多个Master,需要加上一个分布式锁的话,Redis官方推出了一个规范的算法:RedLock。
参考:
Redis的三种集群模式
通过jedis获取分布式锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import redis.clients.jedis.Jedis; import redis.clients.jedis.params.SetParams; import java.util.Collections;
public class RedisLock {
private static final Long RELEASE_SUCCESS = 1L; private static final String LOCK_SUCCESS = "OK"; private static final String KEY_PREFIX="DE_BATCH_LOCK_";
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(KEY_PREFIX+lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; }
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, Integer expireTime) { SetParams params = SetParams.setParams().nx(); if (expireTime != null) { params.ex(expireTime); } String result = jedis.set(KEY_PREFIX+lockKey, requestId, params); if (LOCK_SUCCESS.equals(result)) { return true; } String value=jedis.get(KEY_PREFIX+lockKey); logger.error("try get lock failed,current value = " + value); return false; } }
|
通过Redisson获取分布式锁
1 2 3 4
| RedissonClient client = Redisson.create(config); RLock lock = getClient().getLock(lockKey); lock.tryLock(waitTime, leaseTime, unit);
|
基于Redisson的RedLock实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| RLock lock1 = redissonClient1.getLock(lockKey); RLock lock2 = redissonClient2.getLock(lockKey); RLock lock3 = redissonClient3.getLock(lockKey);
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3); boolean isLock; try { isLock = redLock.tryLock(1, 10, TimeUnit.SECONDS); if (isLock) { } } catch (Exception e) { e.printStackTrace(); } finally { redLock.unlock(); }
|