Redis「7」实现分布式锁
01-基于 Redis 的分布式锁
在同一进程中,为确保不同线程之间的同步操作,一般使用的synchronized
关键字或ReentrantLock
等锁机制。但对不同服务器上的多个进程来说,如果要达到同步操作的效果,就需要借助分布式锁来实现。
一般来说,实现分布式锁有几种方式:1)基于数据库;2)基于 Redis;3)基于 ZooKeeper。本文我们将讨论一下基于 Redis 如何实现分布式锁功能。
01.1-setnx 和 expire
Redis 中的setnx
只有当键不存在时才可设置键的值,可用来表示成功获取锁。未避免进程在持锁期间异常宕机,而导致锁无法及时被释放,可用expire
命令为锁添加一个过期时间,可以在超时后自动释放。
Redis 2.6.12 及更高版本中,set
命令添加了对”set iff not exist”、”set iff exist”和”expire timeout”语义的支持:
01.2-Redis 实现分布式锁的基本逻辑
Redis 中实现分布式锁的基本逻辑如下:
在 Redis 中指定一个键作为锁,指定唯一标识作为值
当 Redis 不包含锁时,意味着无任何进程持锁,此时进程可以请求获取锁,成功后将“锁-自身标识”键值对写入 Redis。同时只有一个进程可以拥有锁,满足互斥性。
为防止死锁,即为避免某个进程异常导致锁无法被释放,需要设置过期时间。
在有效时间内,释放锁时,需要校验唯一标识,避免错误地释放他人的锁。这个过程要保证原子性,一般采用 Lua 脚本实现。
使用 Redis 原生功能实现分布式锁的细节,可以参考 [1]。
02-Redisson
Redisson 是一个供 Java 开发者使用的 redis-cli 实现。它提供了分布式锁和同步机制的实现。Redisson 中提供了多种锁实现,例如可重入锁RLock
,读写锁RReadWriteLock
,以供不同场景中使用。
02.1-可重入锁
使用可重入锁的方式非常简单:
当获取锁以后,在 Redis 中会存在一个键为 anyLock,值类型为 hash,field 为 “a5112130-6121-4fa9-98f6-271a7c724c00:88”(每次都可能会不一样),value 为 1,指重入次数。
02.2-读写锁
使用读写锁的方式如下:
进程获取读锁后,Redis 中会存在一个键为 {read:write:lock}:a5112130-6121-4fa9-98f6-271a7c724c00:90:rwlock_timeout:1,值类型为 string,值为 1,超时时间为 30s。
进程获取写锁后,Redis 中会存在一个键为 read:write:lock,值类型为 hash,包含两个 field: “a5112130-6121-4fa9-98f6-271a7c724c00:89:write”(可能每次都不一样),其值为 1;”mode”,其值为 write / read
读锁与读锁之间互补影响
读-写锁、写-写锁、写-读锁之间互斥,同步
02.3-信号量
当临界资源数为 1 时,使用锁可以控制多进程之间同步访问。当临界资源有多个时,锁的表达能力有限,这时候可以使用信号量来控制多个进程之间的同步。
在使用信号量之前,需要在 Redis 中存储一个键 park,其值为临界资源的数量,例如 3,表示连接资源数量为 3。
每当进程通过 acquire 获取一个信号量,park 的值将减小 1。当信号量为 0 时,尝试获取信号量将失败。进程通过 release 释放信号量时,park 的值将增加 1。
注:这里有个小坑,如果重复调用 release,将导致型号量的值不断增加,使用时需要特别注意。
上述使用示例的完整代码可以在我的 gitee 中找到。
历史文章推荐
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/78ff2a21d36bce705bb102279】。文章转载请联系作者。
评论