25-拓展 3:拾遗补漏 —— 再谈分布式锁
课程
1
开篇:授人以鱼不若授人以渔 —— Redis 可以用来做什么?
学习时长: 5分21秒
2
基础:万丈高楼平地起 —— Redis 基础数据结构
上次学到
学习时长: 16分14秒
3
应用 1:千帆竞发 —— 分布式锁
学习时长: 7分47秒
4
应用 2:缓兵之计 —— 延时队列
学习时长: 8分9秒
5
应用 3:节衣缩食 —— 位图
学习时长: 8分52秒
6
应用 4:四两拨千斤 —— HyperLogLog
学习时长: 14分17秒
7
应用 5:层峦叠嶂 —— 布隆过滤器
学习时长: 17分54秒
8
应用 6:断尾求生 —— 简单限流
学习时长: 4分37秒
9
应用 7:一毛不拔 —— 漏斗限流
学习时长: 7分22秒
10
应用 8:近水楼台 —— GeoHash
学习时长: 7分52秒
11
应用 9:大海捞针 —— Scan
学习时长: 8分42秒
12
原理 1:鞭辟入里 —— 线程 IO 模型
学习时长: 4分1秒
13
原理 2:交头接耳 —— 通信协议
学习时长: 3分34秒
14
原理 3:未雨绸缪 —— 持久化
学习时长: 5分27秒
15
原理 4:雷厉风行 —— 管道
学习时长: 3分51秒
16
原理 5:同舟共济 —— 事务
学习时长: 6分36秒
17
原理 6:小道消息 —— PubSub
学习时长: 7分7秒
18
原理 7:开源节流 —— 小对象压缩
学习时长: 7分14秒
19
原理 8:有备无患 —— 主从同步
学习时长: 4分9秒
20
集群 1:李代桃僵 —— Sentinel
学习时长: 3分52秒
21
集群 2:分而治之 —— Codis
学习时长: 7分28秒
22
集群 3:众志成城 —— Cluster
学习时长: 8分38秒
23
拓展 1:耳听八方 —— Stream
学习时长: 13分40秒
24
拓展 2:无所不知 —— Info 指令
学习时长: 4分4秒
25
拓展 3:拾遗补漏 —— 再谈分布式锁
学习时长: 2分18秒
26
拓展 4:朝生暮死 —— 过期策略
学习时长: 2分21秒
27
拓展 5:优胜劣汰 —— LRU
学习时长: 4分34秒
28
拓展 6:平波缓进 —— 懒惰删除
学习时长: 2分13秒
29
拓展 7:妙手仁心 —— 优雅地使用 Jedis
学习时长: 6分35秒
30
拓展 8:居安思危 —— 保护 Redis
学习时长: 2分19秒
31
拓展 9:隔墙有耳 —— Redis 安全通信
学习时长: 6分34秒
32
拓展 10:法力无边 —— Redis Lua 脚本执行原理
学习时长: 9分24秒
33
拓展 11:短小精悍 —— 命令行工具的妙用
学习时长: 9分21秒
34
源码 1:丝分缕析 —— 探索「字符串」内部
学习时长: 5分20秒
35
源码 2:循序渐进 —— 探索「字典」内部
学习时长: 7分24秒
36
源码 3:挨肩迭背 —— 探索「压缩列表」内部
学习时长: 10分42秒
37
源码 4:风驰电掣 —— 探索「快速列表」内部
学习时长: 3分49秒
38
源码 5:凌波微步 —— 探索「跳跃列表」内部
学习时长: 9分57秒
39
源码 6:破旧立新 —— 探索「紧凑列表」内部
学习时长: 2分42秒
40
源码 7:金枝玉叶 —— 探索「基数树」内部
学习时长: 5分36秒
41
源码 8:精益求精 —— LFU vs LRU
学习时长: 8分4秒
42
源码 9:如履薄冰 —— 懒惰删除的巨大牺牲
学习时长: 9分53秒
43
源码 10:跋山涉水 —— 深入字典遍历
学习时长: 9分24秒
44
源码 11:见缝插针 —— 探索 HyperLogLog 内部
学习时长: 13分3秒
45
尾声:百尺竿头 —— 继续深造指南
学习时长: 2分32秒
juejin_logo copyCreated with Sketch.

拓展 3:拾遗漏补 —— 再谈分布式锁

在第三节,我们细致讲解了分布式锁的原理,它的使用非常简单,一条指令就可以完成加锁操作。不过在集群环境下,这种方式是有缺陷的,它不是绝对安全的。

比如在 Sentinel 集群中,主节点挂掉时,从节点会取而代之,客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁,但是这把锁还没有来得及同步到从节点,主节点突然挂掉了。然后从节点变成了主节点,这个新的节点内部没有这个锁,所以当另一个客户端过来请求加锁时,立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有,不安全性由此产生。

不过这种不安全也仅仅是在主从发生 failover 的情况下才会产生,而且持续时间极短,业务系统多数情况下可以容忍。

Redlock 算法

为了解决这个问题,Antirez 发明了 Redlock 算法,它的流程比较复杂,不过已经有了很多开源的 library 做了良好的封装,用户可以拿来即用,比如 redlock-py。

import redlock

addrs = [{
    "host": "localhost",
    "port": 6379,
    "db": 0
}, {
    "host": "localhost",
    "port": 6479,
    "db": 0
}, {
    "host": "localhost",
    "port": 6579,
    "db": 0
}]
dlm = redlock.Redlock(addrs)
success = dlm.lock("user-lck-laoqian", 5000)
if success:
    print 'lock success'
    dlm.unlock('user-lck-laoqian')
else:
    print 'lock failed'

为了使用 Redlock,需要提供多个 Redis 实例,这些实例之前相互独立没有主从关系。同很多分布式算法一样,redlock 也使用「大多数机制」。

加锁时,它会向过半节点发送 set(key, value, nx=True, ex=xxx) 指令,只要过半节点 set 成功,那就认为加锁成功。释放锁时,需要向所有节点发送 del 指令。不过 Redlock 算法还需要考虑出错重试、时钟漂移等很多细节问题,同时因为 Redlock 需要向多个节点进行读写,意味着相比单实例 Redis 性能会下降一些。

Redlock 使用场景

如果你很在乎高可用性,希望挂了一台 redis 完全不受影响,那就应该考虑 redlock。不过代价也是有的,需要更多的 redis 实例,性能也下降了,代码上还需要引入额外的 library,运维上也需要特殊对待,这些都是需要考虑的成本,使用前请再三斟酌。

扩展阅读

1. 你以为 Redlock 算法真的很完美?

2. Redlock-py 的作者其人趣事

看头像非常酷,这位老哥的 Github 地址是 github.com/optimuspaul

留言
Ctrl + Enter
全部评论(50)
BinK_1783的头像
删除
人间有三愿,一愿识尽世间好人,二愿读尽世间好书,三愿看尽世间好风光。人间最美四月天,祝你三愿皆有所获
点赞
回复
也不想说话的头像
删除
尼玛,就提了一下redlock这个锁名,啥原理也没介绍
2
回复
华北平原的头像
删除
这章有些过于水了。。。
1
回复
wangzhicheng的头像
删除
不想撸代码的程序猿 @ 钱少活多离家远
3年前看不懂时,感觉很牛逼,现在再来看时,感觉:卧槽,说了个啥啊。。。
6
回复
用户4007958994837的头像
删除
分布式原理两章讲的大部分都是怎么用呀
点赞
回复
Lc酱88671的头像
删除
谈了个啥?。。。
1
回复
cornor的头像
删除
工程师
这个小册子也太简略了吧😂
点赞
回复
allenxguo的头像
删除
Go @ TME
就这?
7
回复
m9rco的头像
删除
Devops @ Qihoo 360
我记得以前不只是这点内容啊!!
9
2
删除
看来我看晚了.............
点赞
回复
删除
作者的书不是出版了,书里内容多吗?想买书
点赞
回复
amzexin的头像
删除
就这么简单的介绍一下 ,无法了
总结:分布式锁set nx ex:存在的问题,当client01获取锁成功后,主节点上的锁对应的key未同步到从节点时,此时主节点宕机 从节点中不存在主节点上的key,此时client02申请锁成功导致两个客户端拿到同一把锁;
解决方案:readLock,半数以上的节点同意后才能获取到锁,保证了锁的key数据不会被丢失,因为哨兵选择从节点遵从raft算法,保证数据的完整性,新任期的leader必须有以前任期leader的完整数据
4
回复
SDingBa的头像
删除
我记得以前内容很多的啊,怎么还删除了?
8
回复
more_money的头像
删除
make money @ money make
这太简略了吧~~~ 这本小册过度简化了~~ 只是一个简单指导性的东西~
17
回复
Mr.Right_li的头像
删除
脑裂
点赞
回复
名字还没想好君的头像
删除
链接是英文论文?
点赞
回复
小鹏丶的头像
删除
java
这个标题是不是应该改成拾遗补漏
点赞
回复
钱同志的头像
删除
Redlock更加安全的锁,需要多个Redis实例,注意:他们不分主从,采用大多数的机制。
4
回复
涉蓝的头像
删除
我突然想到 Redlock 是因为 redis主从的分布式锁没同步 的缺陷弄出来的,所以对于已经有了主从集群的业务方考虑Redlock 的锁的时候 ,发现它需要一群主的redis。。。。说明原来的集群用不上得重新部署一套,这不是很浪费么? 那干嘛不用一个单点的redis 专门放锁,反正本身锁的消耗也少。。。
点赞
2
删除
单点故障问题怎么解决,像这样的架构如何维护?
点赞
回复
删除
回复
可能我有点想当然,但我觉得 锁本身开销很小啊,一台redis 就做这一件事情,肯定负载不大,本身当机可能性就不大,感觉加监控报警然后人工处理就够了
单点故障问题怎么解决,像这样的架构如何维护?
点赞
回复
志兵(Subin)的头像
删除
++为了使用 Redlock,需要提供多个 Redis 实例,这些实例之前相互独立没有主从关系。++好像并没有解决最初提到的Redis主从切换时多个线程获得锁的问题吧
2
回复
用户811717646914的头像
删除
无 @ 上海交通大学
请问Java有Redlock算法的类库吗?
点赞
7
删除
Redisson
点赞
回复
删除
Redisson
点赞
回复
查看更多回复
田佳伟的头像
删除
PHP攻城狮
1、redlock加锁是过半实例set成功就算加锁成功,那么少于一半实例set成功的话,就算加锁失败?这样的话就会自动删除已经set成功的锁吗?
2、获取锁的话,是不是只要有1个实例能获取到就算获取到了,还是也是过半实例能get到锁的话 才算get到?
3
2
删除
第一个问题: 少于一半的实例set成功,加锁失败,没有set成功是因为其他实例上有这个当前加锁的key值,当主动关闭锁时会把所有实例上的key删除,所以没有必要删除已经set成功的key。第二个问题:我觉得应该是过半实例才能判断有锁
点赞
回复
删除
2 获取锁就是set, 没有get一说
2
回复

查看全部 50 条回复