redis分布式锁使用须知

最近在规范平台缓存使用时发现,很多业务用到了 reids 分布式锁,但普遍存在一些细节问题,根据这些问题,本文将会尝试去总结分布式锁常见的问题。最后聊聊redis乐观锁。

分布式锁

如果是单机环境,对于并发问题,直接用 java 提供的 synchronized 或 Lock 实现即可,而涉及到多进程环境,那么就需要依赖一个第三方系统来提供锁机制。

redis作为一个缓存中间件系统,就能提供这种分布式锁机制,其本质就是在redis里面占一个坑,当别的进程也要来占坑时,发现已经被占领了,就只要等待稍后再尝试

在java中我们一般这样用:

boolean result = jedis.setnx("lock-key",String.valueOf(System.currentTimeMillis()))== 1L;
if  (result) {
    try {
        // do something
    } finally {
        jedis.del("lock-key");
    }
 }

须知一:潜在死锁

上面程序逻辑存在一个问题,就是如果加锁和解锁中间执行的业务中断,比如服务器挂了,或者线程被杀掉,那么就可能会导致 del 指令没有被调用,这样就会陷入死锁,锁永远得不到释放。

那么实际应用中我们应该给锁加上过期时间,比如5秒,这样即使出现上面说的异常,也可以保证5秒后锁会自动释放。所以程序可以优化成如下:

try {
    boolean result = jedis.setnx("lock-key",String.valueOf(System.currentTimeMillis()))== 1L;
    if  (result) {
        jedis.expire("lock-key",5);
        // do something
    }
} finally {
    jedis.del("lock-key");
}

这样写也存在一个问题:由于 setnx 和 expire 非原子性,如果在 setnx 和 expire 之间出现机器挂掉或者是被人为杀掉,就会导致死锁。

加锁正确姿势:

String result = jedis.set("lock-key", String.valueOf(System.currentTimeMillis(), "NX", "PX", 5);
if ("OK".equals(result)) {
    return true;
}
return false;

须知二:超时问题

Redis 分布式锁并不能解决超时问题,其实基于 ZooKeeper 实现的分布式锁也没办法避免超时问题。

考虑如下场景,加锁和解锁之间的业务非常耗时,那么就可能存在:

  • 线程一拿到锁之后执行业务
  • 还没执行完锁就超时过期了
  • 线程二此时拿到锁乘虚而入,开始执行业务...

当然这是 redis 分布式锁在死锁和超时问题之间做出的妥协,没办法完全避免,但是需要业务在使用时,衡量加锁的粒度及过期时间

须知三:可重入性

可重入性是指线程在持有锁的情况下,再次请求持有同一把锁,那么是可以获取到的。在 java 中, synchronized 和 ReentrantLock 都是可重入锁。

redis本身不具备可重入性,如果要支持可重入锁,可以借助 Threadlocal 对请求的 setnx 进行包装,Threadlocal 变量存储当前持有锁的计数。

须知四:集群环境如何保证锁的安全

redis分布式锁在集群环境下,不是绝对的安全的。比如:主节点的锁还没来得及同步到从节点,此时主节点挂了,从节点取而代之。

线程1在主节点已经成功拿到一把锁,此时切到了从节点,这把锁不存在了,此时线程2轻松在从节点取到这把锁,这就导致一把锁被两个线程拿到了。

Redlock 算法

Redlock 算法就是为了解决这个问题,他的原理是在加锁时,向过半节点发送 set 指令,只要过半节点返回成功,那就认为加锁成功。释放锁时,再向所有节点发送 del 指令。

代价也很明显,跟tomcat 的 session 共享机制一样,随着集群机器的增加,势必会有损性能。Redlock 算法还需要考虑出错重试时钟漂移等很多细节问题。

所以一般这种由于主从节点同步时间差导致的锁不安全问题,业务系统一般都是选择忍受的,生产上这种场景发生的概率也不大。

redis乐观锁

以上讨论的 reids 分布式锁本质就是使用 setnx 指令实现占位功能,所以这种分布式锁是一种悲观锁,我们也可以借助 redis 的 watch 指令实现乐观锁。

实现原理

结合 redis 事务,watch 会在事务开始之前盯住某个变量,当事务执行提交执行时,redis 会自动检查被watch的变量,是否被修改过了,如果变量被修改过,事务提交指令 exec 会返回 null 告知客户端事务执行失败。

举例

场景:redis 存储了用户金额,现在有两个并发请求改账户额度,业务实现上需要获取到金额,再修改金额,最后写入 redis 。

> set account 100
> watch account
OK
> set account  50 # 事务执行过程中被修改
OK
> multi # 开始事务
OK
> incr account # 账户额度+1
QUEUED
> exec  # 事务提交,返回失败
(nil)

当 exec 指令返回一个 null 时,客户端知道了事务执行是失败的。

注意

  • 上面例子中的 multi/exec 对应数据库中的 begin/commit,discard 对应 rollback
  • Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。

参考:
Redis 分布式锁的正确实现方式( Java 版 )
Redis 设计与实现 -- 事务

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容