如何实现靠谱的分布式锁技术Word格式文档下载.docx
- 文档编号:13315804
- 上传时间:2022-10-09
- 格式:DOCX
- 页数:10
- 大小:149.95KB
如何实现靠谱的分布式锁技术Word格式文档下载.docx
《如何实现靠谱的分布式锁技术Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《如何实现靠谱的分布式锁技术Word格式文档下载.docx(10页珍藏版)》请在冰豆网上搜索。
1.基于Redis实现的锁服务
加锁流程
SETresource_namemy_random_value
NXPXmax-lock-time
注:
资源不存在时才能够成功执行set操作,用于保证锁持有者的唯一性;
同时设置过期时间用于防止死锁;
记录锁的持有者,用于防止解锁时解掉了不符合预期的锁。
解锁流程
ifredis.get("
resource_name"
)==
"
my_random_value"
returnredis.del
("
)elsereturn0
使用Lua脚本保证获取锁的所有者、对比解锁者是否所有者、解锁是一个原子操作。
该方案的问题在于:
1.通过过期时间来避免死锁,过期时间设置多长对业务来说往往比较头疼,时间短了可能会造成:
持有锁的线程A任务还未处理完成,锁过期了,线程B获得了锁,导致同一个资源被A、B两个线程并发访问;
时间长了会造成:
持有锁的进程宕机,造成其他等待获取锁的进程长时间的无效等待。
2.Redis的主从异步复制机制可能丢失数据,会出现如下场景:
A线程获得了锁,但锁数据还未同步到slave上,master挂了,slave顶成主,线程B尝试加锁,仍然能够成功,造成A、B两个线程并发访问同一个资源。
2、基于ZooKeeper实现的锁服务
1.首先在/resource_name节点下创建临时有序节点。
2.获取当前线程创建的节点及/resource_name目录下的所有子节点,确定当前节点序号是否最小,是则加锁成功。
否则监听序号较小的前一个节点。
ZAB一致性协议保证了锁数据的安全性,不会因为数据丢失造成多个锁持有者;
心跳保活机制解决死锁问题,防止由于进程挂掉或者僵死导致的锁长时间被无效占用。
具备阻塞锁特性,并通过Watch机制能够及时从阻塞状态被唤醒。
解锁流程是删除当前线程创建的临时接点。
该方案的问题在于通过心跳保活机制解决死锁会造成锁的不安全性,可能会出现如下场景:
持有锁的线程A僵死或网络故障,导致服务端长时间收不到来自客户端的保活心跳,服务端认为客户端进程不存活主动释放锁,线程B抢到锁,线程A恢复,同时有两个线程访问共享资源。
基于上诉对现有锁方案的讨论,我们能看到,一个理想的锁设计目标主要应该解决如下问题:
1.锁数据本身的安全性。
2.不发生死锁。
3.不会有多个线程同时持有相同的锁。
而为了实现不发生死锁的目标,又需要引入一种机制,当持有锁的进程因为宕机、GC活者网络故障等各种原因无法主动过释放锁时,能够有其他手段释放掉锁,主流的做法有两种:
1.锁设置过期时间,过期之后Server端自动释放锁。
2.对锁的持有进程进行探活,发现持锁进程不存活时Server端自动释放。
实际上不管采用哪种方式,都可能造成锁的安全性被破坏,导致多个线程同时持有同一把锁的情况出现。
因此我们认为锁设计方案应在预防死锁和锁的安全性上取得平衡,没有一种方案能够绝对意义上保证不发生死锁并且是安全的。
而锁一般的用途又可以分为两种,实际应用场景下,需要根据具体需求出发,权衡各种因素,选择合适的锁服务实现模型。
无论选择哪一种模型,需要我们清楚地知道它在安全性上有哪些不足,以及它会带来什么后果。
1.为了效率,主要是避免一件事被重复的做多次,用于节省IT成本,即使锁偶然失效,也不会造成数据错误,该种情况首要考虑的是如何防止死锁。
2.为了正确性,在任何情况下都要保证共享资源的互斥访问,一旦发生就意味着数据可能不一致,造成严重的后果,该种情况首要考虑的是如何保证锁的安全。
下面主要介绍一下SharkLock的一些设计选择。
锁信息设计如下
·
lockBy:
Client唯一标识。
condition:
Client在加锁时传给Server,用于定义Client期望Server的行为方式。
lockTime:
加锁时间。
txID:
全局自增ID。
lease:
租约。
如何保证锁数据的可靠性
SharkLock底层存储使用的是SharkStore,SharkStore是一个分布式的持久化Key-Value存储系统。
采用多副本来保证数据安全,同时使用raft来保证各个副本之间的数据一致性。
如何预防死锁
1.Client定时向Server发送心跳包,Server收到心跳包之后,维护Server端Session并立即回复,Client收到心跳包响应后,维护Client端Session。
心跳包同时承担了延长Session租约的功能。
2.当锁持有方发生故障时,Server会在Session租约到期后,自动删除该Client持有的锁,以避免锁长时间无法释放而导致死锁。
Client会在Session租约到期后,进行回调,可选择性的决策是否要结束对当前持有资源的访问。
3.对于未设置过期的锁,也就意味着无法通过租约自动释放故障Client持有的锁。
因此额外提供了一种协商机制,在加锁的时候传递一些condition到服务端,用于约定Client端期望Server端对异常情况的处理,包括什么情况下能够释放锁。
譬如可以通过这种机制实现Server端在未收到十个心跳请求后自动释放锁,Client端在未收到五个心跳响应后主动结束对共享资源的访问。
4.尽最大程度保证锁被加锁进程主动释放。
进程正常关闭时调用钩子来尝试释放锁
未释放的锁信息写文件,进程重启后读取锁信息,并尝试释放锁。
如何确保锁的安全性
1.尽量不打破谁加锁谁解锁的约束,尽最大程度保证锁被加锁进程主动释放。
a)进程正常关闭时调用钩子来尝试释放锁。
b)未释放的锁信息写文件,进程重启后读取锁信息,并尝试释放锁。
2.依靠自动续约来维持锁的持有状态,在正常情况下,客户端可以持有锁任意长的时间,这可以确保它做完所有需要的资源访问操作之后再释放锁。
一定程度上防止如下情况发生。
a)线程A获取锁,进行资源访问。
b)锁已经过期,但A线程未执行完成。
c)线程B获得了锁,导致同时有两个线程在访问共享资源。
3.提供一种安全检测机制,用于对安全性要求极高的业务场景。
a)对于同一把锁,每一次获取锁操作,都会得到一个全局增长的版本号。
b)对外暴露检测APIcheckVersion(lock_name,version),用于检测持锁进程的锁是不是已经被其他进程抢占(锁已经有了更新的版本号)。
c)加锁成功的客户端与后端资源服务器通信的时候可带上版本号,后端资源服务器处理请求前,调用checkVersion去检查锁是否依然有效。
有效则认为此客户端依旧是锁的持有者,可以为其提供服务。
d)该机制能在一定程度上解决持锁A线程发生故障,Server主动释放锁,线程B获取锁成功,A恢复了认为自己仍旧持有锁而发起修改资源的请求,会因为锁的版本号已经过期而失败,从而保障了锁的安全性。
下面对SharkLock依赖的SharkStore做一个简单的介绍。
SharkStore基本模块
MasterServer集群分片路由等元数据管理、扩容和Failover调度等。
DataServer数据存储节点,提供RPC服务访问其上的KV数据。
GatewayServer网关节点,负责用户接入。
Sharding
SharkStore采用多副本的形式来保证数据的可靠性和高可用。
同一份数据会存储多份,少于一半的副本宕机仍然可以正常服务。
SharkStore的数据分布如下图所示。
扩容方案
当某个分片的大小到达一定阈值,就会触发分裂操作,由一个分片变成两个,以达到扩容的目的。
Dataserver上range的leader自己触发。
leader维持写入操作字节计数,每到达checksize大小,就异步遍历其负责范围内的数据,计算大小并同时找出分裂时的中间key如果大小到达splitsize,向master发起AskSplit请求,同意后提交一个分裂命令。
分裂命令也会通过raft复制到其他副本。
本地分裂。
分裂是一个本地操作,在本地新建一个range,把原始range的部分数据划拨给新range,原始range仍然保留,只是负责的范围减半。
分裂是一个轻量级的操作。
Failover方案
failover以range的级别进行。
range的leader定时向其他副本发送心跳,一段时间内收不到副本的心跳回应,就判断副本宕机,通过range心跳上报给master。
由master发起failover调度。
Master会先删除宕机的副本然后选择一个合适的新节点,添加到range组内之后通过raft复制协议来完成新节点的数据同步。
Balance方案
dataserver上的rangeleader会通过range心跳上报一些信息,每个dataserver还会有一个节点级别的Node心跳。
Master收集这些信息来执行balance操作。
Balance通过在流量低的节点上增加副本,流量高的节点上减少副本促使整个集群比较均衡,维护集群的稳定和性能。
Raft实践:
MultiRaft
1.心跳合并
以目标dataserver为维度,合并dataserver上所有Raft心跳心跳只携带rangeids,心跳只用来维护leader的权威和副本健康检测rangeids的压缩,比如差量+整型变长Leader类似跟踪复制进度,跟踪followercommit位置。
2.快照管理控制
建立ACK机制,在对端处理能力之内发送快照;
控制发送和应用快照的并发度,以及限速;
减少对正常业务的冲击。
Raft实践-PreVote。
Raft算法中,leader收到来自其他成员term比较高的投票请求会退位变成follower因此,在节点分区后重加入、网络闪断等异常情况下,日志进度落后的副本发起选举,但其本身并无法被选举为leader,导致集群在若干个心跳内丢失leader,造成性能波动;
针对这种情况,在raft作者的博士论文中,提出了prevote算法:
在发起选举前,先进行一次预选举Pre-Candidate,如果预选举时能得到大多数的投票,再增加term,进行正常的选举。
prevote会导致选举时间变长(多了一轮
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 如何 实现 分布式 技术