分布式锁解决方案
2020-09-23 11:22:37 1 举报
AI智能生成
分布式锁
作者其他创作
大纲/内容
分布式锁解决方案
分布式锁需要考虑的几个点?
可重入or不可重入
公平or非公平
阻塞or非阻塞
独占or非独占
基于Redis单节点
SET lock_key random_value NX PX 5000
缺点:不具备可重入性
Redission
可重入锁
用hash数据结构。key=锁名称,hkey=随机字符串:线程ID,hvalue=自增Int
重入值通过hvalue大小来说明
LUA脚本:1. 用exist判断,如果不存在,则加锁成功;2. 用hexist判断,如果存在并且是当前线程,说明是重入锁,加锁成功;3. 如果不是当前线程,说明是其他线程持有说,加锁失败;
Redission同时支持RedLock
用set数据结构实现
源码解析
基于Redis多节点的RedLock
高可用?分布式环境下会遇见的问题?
1. 崩溃恢复
解决方案:崩溃延迟重启
2. 时钟跳跃
解决方案:1. 采用小步快跑方式,多次修改,每次更新时间量小2. 通过阈值来做判断
3. GC STW、缺页故障、网络延迟
antirze认为redlock做了一些微小的工作,但是没办法完全避免,其他分布式锁方案也没有办法
ZK分布式锁也解决不了这个问题
RedLock具体算法
RedLock是建立在一个Time可信模型上
1. 获取开始时间;2.去各节点获取锁;3.再次获取时间;4. 计算获取锁的实践,检查获取锁的时间是否小于锁的过期时间;5. 如果小于,持有锁
1-3步之间发生了阻塞,RedLock可以感知锁已经过期,但是#4之后发生阻塞怎么办?
答案是:其他分布式锁方案也没有解决这个问题
基于Redis的分布式锁到底安全吗(下)?
基于DB
基于DB排他锁(悲观锁)
方式: public boolean lock(){ connection.setAutoCommit(false) while(true){ try{ result = select * from methodLock where method_name=xxx for update; if(result==null){ return true; } }catch(Exception e){ } sleep(1000); } return false; }
要给method_name添加唯一性索引
1. 会不会阻塞?
其他事务如果select... for update失败,则会一直阻塞
阻塞也会带来线程池撑爆风险
2. 是否可重入?
不可重入
缺点
1. 使用不当会死锁
2. 性能上有额外开销
3. 加锁列如果不加索引会导致表锁
基于DB表记录的唯一性索引
瑜伽案例
锁没有失效时间,如果解锁失败会一直留在数据库中;可以用定时任务去清理
不可重入,同一个线程在释放之前无法再次获取锁
主从同步延迟?
优点
非阻塞式
基于DB乐观锁
不依赖DB锁
并发小的时候,有少量请求会失败;大促、秒杀场景下,会有大量请求作用于同一条行锁,对数据库有很大的写压力
适用场景
并发量不高,写不频繁的场景
1. 在数据量比较少的时候,会进行表锁,而不是行锁
2. 需要占用DB连接
3. 依赖数据库,避免单点
基于ZK
独占锁
容易引发羊群效应
共享锁
1. Client创建create类似/lockpath/{hostname}-读写类型-序号的临时有序节点2. Client创建完之后,通过getChildren获取节点下所有子节点,并且对所有节点注册Watch监听。3. 确定本次创建的节点序号在所有节点中的顺序a. 对于读请求,ⅰ. 如果没有比自己更小的子节点,后者比自己更小的子节点都是读请求,则获取锁ⅱ. 如果比自己更小的节点中有写请求,那么等待;b. 对于写请求,ⅰ. 如果自己不是最小的节点,则等待;4. 等待监听通知后,重复步骤1
羊群效应?
改进后的分布式锁:1. Client调用create创建一个/lockpath/{hostname}-读写类型-序号的临时有序节点。2. Client调用getChildren获取所有子节点列表,这里不注册Watch监听3. 确定本次创建的节点序号在所有节点中的顺序a. 如果是读请求,向比自己小的写请求注册Watch监听b. 如果是写请求,向比自己小的最后一个节点注册Watch监听4. 等待监听后,继续执行步骤2。
基于curator实现
ZK是如何检测Client已经崩溃?
依靠心跳session来维护锁的持有状态
分布式锁选择?
从性能角度
缓存》ZK》=数据库
从高可用角度
ZK》缓存》数据库
从复杂角度(从低到高)
0 条评论
回复 删除
下一页