左耳听风_053_52_管理设计篇之分布式锁

你好,我是陈浩网名左耳朵house.这节课呢我们来讲分布式锁。

我们知道啊在多线程的情况下访问一些共享资源呢需要加锁,不然呢就会出现数据被写乱的问题。

在分布式系统下这样的问题啊也是一样的。

只不过呢我们需要一个分布式的锁服务。

对于分布式的锁服务呢,一般可以用数据库DBREDIS和zookeeper等实现。

那不管怎么样啊,分布式的锁服务啊需要有这么几个特点。

那第一呢就是安全性。

Safety在任意的时刻呢,只有一个客户端可以获得锁。

那第二呢就是避免死锁,就是客户端啊,最终一定。可以获得锁。

即使呢锁住某个资源的客户端,在释放锁之前崩溃了,或者呢网络不可达那。

第三呢是容错性,只要锁服务集群中的大部分节点存活,那客户端就可以进行加锁解锁操作。

那这里呢我提一下避免思锁的问题。

那接下来呢我会以REDIS的锁服务为例,你可以参考REIX的官方文档。

我们通过文中这行命令对资源加速。

那这里呢我解释一下,首先呢set NX命令只会在key不存在的时候给key赋值,而PX命令呢通知REDIS保存这个k三万毫秒。

那其次呢my redom value啊必须是全局唯一的值。

那这个随机数在释放锁的时候呢,保证释放锁操作的安全性。

另外呢PX操作后面的参数代表的是这个t的存活时间啊,称作锁过期的时间。

当资源被锁定超过这个时间的时候呢,锁就会自动释放获得锁的客户端。

如果没有在这个时间窗口内完成操作,就可能啊会有其他的客户端获得锁,引起征用的问题。

那这里的原理啊就是只有在某个key不存在的情况下才能设置成功这个key.于是呢这就可以让多个进程啊并发的去设置同一个key.但只有一个进程能设置成功,而其他的进程呢?因为之前有人把key设置成功了而导致失败啊,也就是获得锁失败。

然后呢,我们就可以通过文中的这段脚本给申请成功的锁解锁。

那如果key对应的value一致可删除这个key.那通过这个方式释放锁呢,是为了避免client释放了其他client申请的锁。

那如果不区分client呢,就会出现一些问题。

我举个例子,首先呢client a获得了一个锁,然后呢当尝试释放锁的请求发送给REDIS的时候呢,被阻塞了啊,没有及时到达REDDIS,于是呢锁定时间就超时了。

Reddiis认为啊锁的租约到期了啊,释放了这个锁,所以呢卡拉恩b重新申请到了这个锁。

那这个时候呢client a的解锁请求到达了将client b锁降的key解锁。

那接下来呢clan c也获得了这个锁,那这样呢就会导致client b和client c同时持有锁。

那通过执行以上的脚本方式释放锁呢克拉ent的解锁操作,只会解锁自己曾经枷锁的资源。

所以呢是安全的那关于value的生成呢,官方推荐啊。

第第v有random地址址啊,取二十个字节作为随机数,或者呢采用更加简单的方式啊,例如使用二c四加密算法,在第一VA rrandom中得到一个种子seed,然后呢生成一个伪随机流,也可以采用更简单的方法使用时间戳,加上客户端编号的方式生成随机数。

那REDIS的官方文档说呢,这种方式的安全性啊要差一些,但是对于绝大多数的场景来说已经足够安全了。

注意啊,虽然REDIS文档里面说他们的分布式锁是没有问题的,但是呢其实还是很有问题的啊,尤其是上面那个为了避免panent端把锁占住不释放,然后呢REDI在超市后把它释放掉的操作啊,不知道你怎么想,但是我觉得这个事儿听起来就有点不靠谱。

我们来脑补一下,不难发现这样一个案例,假如client a呢先取得了锁其他client,比如说client b在等待client a的工作完成了。

那这个时候呢如果client a被挂在了某些事儿上,比如一个外部的阻塞调用,或者呢CPU被别的进程吃满了,或者呢不巧碰上了负GC,导致clan a啊花了超过平时几倍的时间。

然后呢,我们的锁夫因为怕死锁就在一定时间之后啊,把锁给释放掉了。

那这个时候呢,感恩b获得了锁,并更新了资源。

那过了一会儿呢,client a服务缓过来了,然后呢也去更新了资源。

于是呢就把cllient b的更新给冲掉了。

那这个呢就造成了数据突错那,这个听起来还挺严重的吧。

果子文中呢给你画了一张图,展示了这个事例,千万不要以为啊这是脑补出来的案例。

其实呢这个是真实案例h base啊,就曾经遇到过这样的问题。

你可以在他们的PPT中啊看到相关的描述。

那要解决这个问题呢,你需要引入fans技术啊,也就是栅栏技术。

那一般来说呢这就是乐观锁机制啊,需要一个版本号做排他。

那这样我们的流程啊就变成了文中第二幅图这个样子。

我们从图中呢可以看到,锁服务呢需要有一个单调递增的版本号,而且写数据的时候呢也需要带上自己的版本号。

那数据库服啊还需要保存数据的版本号啊,然后对请求呢做检查。

那如果你使用zookeeper做锁服务的话,那么可以使用ZXID或者z note版本号来做这个fans版本号。

但是呢我们想一想,如果数据库中也保留着版本号,那么完全可以用数据库来做这个锁服,不就更方便了吗?那文中这张图啊就展示了这个过程使用数据版本记录机制啊,也就是为数据啊增加一个版本标识。

一般呢是通过为数据库表增加一个数字类型的version字段来实现的当读取数据的时候呢,像version ons用的值啊一同读出数据,每更新一次就对这个version加一。

当我们提交更新的时候呢,数据库表对应记录的当前版本信息与第一次取出来的version时进行比对。

那如果数据库表当前版本的话,与第一次取出来的version值相等就予以更新,否则呢就认为是过期数据。

那更新语句写成server啊,大概就是文中这个样子,你可以参考一下。

你可能会发现啊,这不就是乐观锁吗?是的,这个就是乐观锁最常用的一种实现方式。

如果我们使用版本号或者fans toven这种方式啊,就不需要使用分布式锁服务了。

另外呢多说一下这种fans token的玩法。

在数据库那边一般会用用ststep时间戳来玩啊,也是在更新提交的时候,检查当年数据库中啊数据的时间戳和自己更新前取到的时间戳进行对比。

那如果一致呢,则OK,否则呢就是版本的冲突。

还有呢我们有时候都不需要增加额外的版本字段或者fans token.比如呢如果我想更新库存,我们呢就可以这么操作,就是先把库存数量呢查出来。

然后呢在更新的时候检查一下是否是上次读出来的库存。

那如果不是呢就说明有别人更新过了,我的update操作就会失败啊,得重新再来。

细心的你一定会发现,这不就是计算机汇编指令中的原子操作CAS啊,也就是compare and swam吗?大量无锁的数据结构啊都需要用的这个关于CS的话题呢,你可以看一下我在酷share写的无所队列的实现,我们一步一步的从分布式锁服务到乐观锁,再到CES.你看到了什么呢?你是否会思考一个有趣的问题,就是我们还需要分布式锁服务吗?那最后呢我们来谈一谈分布式所设计的重点。

一般情况下,我们可以使用数据库REDIS或者zookeeper来做分布式锁服务这几种方式啊都可以用于实现分布式锁。

那分布式锁的特点呢就是保证在一个集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。

那这个呢就是所谓的分布式互斥。

所以大家在做某个事儿的时候呢,要去一个服务上请求一个标志。

那如果请求到了呢,我们就可以操作操作完成之后啊,把这个标志还回去。

那这样别的进程啊就可以请求到了。

首先呢我们需要明确一下分布式锁服务的初衷啊和几个概念性的问题。

那第一呢如果获得锁的进程挂掉了怎么办呢?锁还不回来了,会导致死锁。

那一般的处理方法呢是在锁服务那边加上一个过期时间。

如果在这个时间内啊锁没有被还回来,那么锁夫呢就要自动解锁,以避免全部锁住。

那第二呢,如果锁服务自动解锁了,新的进程呢就拿到锁了。

但是呢之前的进程啊以为自己还有锁,那么呢就会出现两个进程,拿到了同一个锁的问题。

他们在更新数据的时候呢就会产生问题。

那对于这个问题呢,我想在这里啊说一下,首先呢像REDIS那样,也可以使用check and set的方式来保证数据的一致性。

那这个呢有点像计算机原子指令CES一样。

就是说啊我在改变一个值的时候呢,先检查一下是不是我之前读出来的值,这样来保证期间没有人改过。

另外呢如果通过像CES这样的操作的话,我们还需要分布式锁服务吗?的确是不需要的啊,不是吗?但是呢现实生活中啊也有不需要更新某个数据的场景啊,只是为了同步或者互试一下不同机器上的线程。

那这个时候呢像REDIS这样的分布式锁夫啊就有意义了。

所以呢我们需要分清楚我是用来修改某个共享源的,还是用来不同进程间的同步或者互斥的那如果使用CES这样的方式来更新数据,那么我们呢是不需要使用分布式锁服务的,而后者呢可能是需要的。

所以呢这就是我们在决定使用分布式锁服务前啊需要考虑的第一个问题啊,就是我们啊是否需要。

那如果确定要分布式锁服务呢,你需要考虑几个设计。

那第一呢就是需要给一个锁被释放的方式来避免请求者,不把锁还回来,导致死锁的问题。

Rredis呢使用超时时间组shiper呢可以依靠自身的session time out来删除节点。

那第二呢就是分布式锁服啊,应该是高可用的,而且呢是需要持久化的。

对此呢你可以看一下reduoed的文档,red lock看一看它是怎么做到高可用的。

嗯,第三呢是要提供非阻塞方式的锁服务。

最后呢还要考虑锁的可重入性。

我认为啊REDIS也是不错的,zookeeper在使用起来啊需要有一些变动的方式。

好在阿帕奇呢有curator,帮我们封装了各种分布式锁的玩法。

好了,我们来总结一下今天分享的主要内容。

首先呢我介绍了为什么需要分布式锁,就像单机系统上的多线程程序,需要用操作系统锁或者数据库锁来忽视。

对共享资源的访问一样,分布式程序呢也需要通过分布式锁来忽视对共享资源的访问。

那分布式锁服呢一般可以通过REDIS和zookeeper来实现。

那接着呢我以REDIS为例,介绍了怎样用它来加锁和解锁,以此呢引出了锁超市后的潜在风险。

我们看到类似于数据库的漏观并发控制。

那这种风险呢可以通过版本号的方式来解决。

那进一步呢数据库如果本身利用CES等手段支持这种版本控制的方式,那其实呢也就没必要用一个独立的分布式锁服务了。

最后呢我们发现分布式锁夫呢还可以用来做同步,那这个呢是数据库锁做不了的事情。

在下节课中呢,我们会聊一聊配置中心相关的技术,希望能对你有帮助。

也欢迎你在留言区分享一下哪些场景下你会用到锁呢?你都用哪种平台的锁服务呢?有没有用到数据库锁呢?是OTC还是悲观锁呢?那如果是悲观锁的话,你又是怎么避免死锁的呢?我在文末呢给出了分布式系统设计模式系列文章的目录,希望你能在这个列表里啊找到自己感兴趣的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值