RedLock
是一款基于 RedisModule
实现的分布式锁模块,该模块提供了写/读锁的操作接口,相比于使用 Redis 的现有命令进行支持,这个模块的命令语义更明确,且有利于进行流量区分与筛选,在使用中能够很好的区分开其他流量与锁操作相关的流量。整体的模块实现比较简单,阅读相对比较容易。
一、简介
- GitHub 地址:https://github.com/wujunwei/redlock/
二、架构设计
2.1、相关命令
- slock.lock : 新增加一个写锁,可以指定该锁的过期时间;
- slock.unlock : 解锁之前的一个写锁,只有具有相同的客户端id的链接才可以解锁;
- slock.rlock : 新增加一个读锁,可以指定该锁的过期时间,多个客户端同时获取写锁时会影响该锁的读的引用计数;
- slock.runlock : 解锁之前的一个读锁,实际是减少对应锁的引用计数,当引用计数减少为0时,删除该key;
- slock.info : 获取锁的信息;
2.2、数据结构
// RedLock结构体 |
2.3、持久化
2.3.1、RDB的持久化
RDB 的存储过程比较简单,直接把对应结构体的所有信息持久化到 RDB 文件中。
void SLockRdbSave(RedisModuleIO *rdb, void *value) { |
2.3.2、AOF的持久化
AOF 的存储过程相当于把现有的锁转换成 slock.lock
和 slock.rlock
命令进行存储。(注意:转储的时候没有记录对应锁的过期时间,重新加载的所有锁都不会过期,算是一个异常);
void SLockAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { |
三、问题与思考
3.1、问题
- 写锁的释放方式依赖于加锁的客户端释放或者到达过期时间,但是如果加锁的客户端异常断开之后,重新连接之后新客户端的id与加锁的客户端id也不同了,这时就只能等待对应锁过期释放,这里关于加锁与释放锁的关系不可靠,极容易出现问题;
- AOF 的持久化过程中没有覆盖过期属性,结合上一个问题,服务重启之后锁永远都不会被释放;
3.2、思考
- 该锁模块没有封装更为高级的接口或命令,相比于Redis现有的命令,没有特别明显的优势,我们完全可以使用以下的命令来替换对应该模块中的命令,在替换的过程中由于读锁和写锁的 value 格式不同,可以通过设置不同的 key 的格式来进行区分。
- slock.lock :
- 思路 : 使用 string 类型数据替换即可;
- 替换为 :
set key_w value nx px expire
- slock.unlock :
- 思路 : 解锁之前需要判断加锁的归属,因此需要使用 lua 脚本;
- 替换为 :
eval "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 key_w value
- slock.rlock :
- 思路 : 保持多个客户端访问同一个读锁,又要确保能够表示引用计数,可以使用 string 数据类型并使用数字 value ;
- 替换为 :
incr key_r
- slock.runlock : 替换为
decr key_r
- slock.info : 替换为
get key_r
- slock.lock :