简单梳理Redis分布式锁 知识点

应用场景

当多个应用进程(客户端)需要互斥地访问共享资源时,可以使用分布式锁。其中Redis官方权威提出了RedLock。Java中可使用Redssion提供的实现。

注意点:

  1. 互斥,保证任何时刻只能有一个客户端 获取到锁;
  2. 效率之死锁,保证获取到锁的客户端即使在出现网络分区或者宕机的情况下,也能释放掉锁;
  3. 效率之容错,保证只要大多数Redis节点正常工作,客户端就能正常获取到锁和正常释放锁。

原理:
获取锁:

SET resource_name my_random_value NX [EX|PX] 30000

Redssion中的实现使用了Hash, 可重入:

if (redis.call(‘exists’, KEYS[1]) == 0)
then redis.call(‘hset’, KEYS[1], ARGV[2], 1);
redis.call(‘pexpire’, KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call(‘hexists’, KEYS[1], ARGV[2]) == 1)
then redis.call(‘hincrby’, KEYS[1], ARGV[2], 1);
redis.call(‘pexpire’, KEYS[1], ARGV[1]);
return nil;
end;
return redis.call(‘pttl’, KEYS[1]);
Lua脚本解释
如果锁对应的Hash, key=resource_name的数据不存在,
那么就设置:
hset resource_name my_random_value 1
pexpire resource_name 30000
如果锁对应的Hash下, fieldKey=my_random_value存在,
那么对其加1,即执行:
hincrby resource_name my_random_value 1
pexpire resource_name 30000
最后返回pttl resource_name

释放锁:

if redis.call(“get”,KEYS[1]) == ARGV[1] then
return redis.call(“del”,KEYS[1])
else
return 0
end

Redisson的释放锁实现:

if (redis.call(‘hexists’, KEYS[1], ARGV[3]) == 0)
then return nil;
end;
local counter = redis.call(‘hincrby’, KEYS[1], ARGV[3],
-1);
if (counter > 0)
then redis.call(‘pexpire’, KEYS[1], ARGV[2]);
return 0;
else redis.call(‘del’, KEYS[1]);
redis.call(‘publish’, KEYS[2], ARGV[1]);
return 1;
end;
return nil;

Redis分布锁常见的几种实现方案:

  1. 单机
    缺点:单点故障,整个服务无法使用。

  2. 主从复制
    缺点:failover问题。 根本原因在于主从复制有延迟。当master在slave复制成功前宕机,slave晋升为master时没有宕机时的锁信息,导致有可能出现两个客户端同时持有同一把锁。
    举例:
    a. client A 在master拿到锁
    b. master节点把A创建的锁信息同步到slave之前宕机了
    c. slave晋升为master节点
    d. client B 在新master拿到与A相同的锁(此时A以为自己仍持有锁)

…TODO

Redisson使用DEMO代码片段:

使用Redisson加锁 释放锁业务代码
简单梳理Redis分布式锁 知识点_第1张图片简单梳理Redis分布式锁 知识点_第2张图片简单梳理Redis分布式锁 知识点_第3张图片

你可能感兴趣的:(NoSQL,Redis)