一个悲观锁和乐观锁的小白故事

640?wx_fmt=jpeg

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

在大型分布式系统中,不可避免的会遇到大量并发写入的场景。在这种场景下如何进行更好的并发控制,即在多个任务同时存取数据时保证数据的一致性,成为分布式系统必须解决的问题。


悲观并发控制和乐观并发控制是并发控制中采用的主要技术手段,对于不同的业务场景,应该选择不同的控制方法也让开发者头疼,今天让通过一个故事(来源:荒唐的程序猿),讲解下其原理和优劣势。


搬好板凳,故事马上开始了。


旺财和小强生活在一个网上商城的系统中, 是一对儿线程好基友。 


星期一刚上班,旺财接到领导电话说,要把一个商品的库存减少20, 旺财不敢怠慢,赶快把库存取出来一看,哦,现在有1000个。


与此同时,小强也接到电话说要把同一商品的库存减少30,他一看,哦,现在有1000个。


旺财计算出最新的库存值980,保存!


小强也计算出最新的库存值970,保存 !  


旺财的数据被小强覆盖了!


领导一看,本来卖出了50个商品,现在库存只扣了30个,这样持续下去就天下大乱了。 


旺财和小强,各打二十大板,长长记性!


小强说:“哥,要不我们还是想个办法吧,再这样下去要被打死的。”


旺财悲催地说: “这样, 以后我们每次访问库存之前,都要先加锁,加了锁,就禁止别人再进入访问,只能等待持有锁的人来释放。”


星期二, 领导让旺财再次把库存减少20 , 旺财这次万分小心,先把库存给锁住,然后慢慢修改。 


小强也接到了把库存减少的指令, 但是旺财哥已经把库存锁住了, 不能操作,小强只好去阻塞车间喝茶聊天,然后到就绪车间等待调度运行。 


好不容易等到可以再次执行了,小强一看,这库存怎么还锁着呢!?只好再次去阻塞车间喝茶。 


领导一看,小强你怎么回事,老是喝茶聊天?还干不干活了?


小强争辩说旺财哥一直锁着库存,我没法操作。

领导不管这些,把小强和旺财又打了二十大板。 


备注:这种加锁的方式就是悲观锁了,悲观锁正如其名,每次取读写数据时候总认为数据会被别人修改,所以将数据加锁,置于锁定状态, 不让别人再访问。缺点是如果持有锁的时间太长,其他用户需要等待很长时间。


旺财说: “兄弟,这一次哥对不住你啊,处理得慢了一些,不过哥刚才挨打的时候想了一个好办法:乐观锁。”


小强说:“拉倒吧你,屁股都快被打烂了还乐观?”


“你听我说嘛, 我们在那个库存字段的旁边,再加上一个版本(version)的字段, 例如刚开始的时候(库存= 1000, 版本=1), 每次你去读的时候不仅要读出库存,还要读出版本号, 等到你修改了库存,往回写的时候一定要检查一下版本号,看看和你读的时候是否一样。”


“如果不一样呢?”  小强问


“那就放弃这次写的操作,重新读取库存和版本号, 重新来过。”


“如果一样呢? ”


“那就放心大胆地把新的库存值写回去。把版本号也加1”


“我似乎有点明白了,我们试试,不过你要想好,我可不想再挨板子了。”


星期三, 旺财奉命把库存减去30, 他先读到了(库存= 1000, 版本=1), 小强也要改库存了,他要把库存减去50,于是他也读到了(库存= 1000, 版本=1)。 


旺财计算出新的库存值970 ,写回成功,现在版本变成了(库存= 970, 版本=2)。 


小强也计算出新库存950 ,也准备写回,他战战兢兢地去看最新的版本号,已经变成版本2了, 按照之前的约定,只好重新读取了。


小强再次读取(库存= 970,版本=2),计算出最新缓存值920(970减去50),再次试图更新,没想到又被别人抢了先,现在版本号已经变成3了 ,最新的数据是(库存= 900,版本=3)。


唉, 只好重新来过, 计算出最新缓存值850(900减去50),第三次终于更新成功了, 最新的库存是 (库存=850, 版本= 4)

领导看到旺财和小强忙得热火朝天,一刻不停,并且库存值也没有乱掉, 满意地点了点头:好同志啊。


备注: 这种方式就是所谓的乐观锁了,旺财和小强这次乐观了一点,觉得一般情况下不会有太多人修改库存,所以没有加锁,放心地去操作,只有在最后更新的时候才去看是否冲突。 这种方式适合于冲突不多的场景,如果冲突很多,数据争用激烈,会导致不断地尝试,反而降低了性能。

故事完


两种锁机制总结:


悲观锁(全称Pessimistic Concurrency Control,缩写PCC)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。


在悲观锁的场景下,假设用户A和B要修改同一个文件,A在锁定文件并且修改的过程中,B是无法修改这个文件的,只有等到A修改完成,并且释放锁以后,B才可以获取锁,然后修改文件。


由此可以看出,悲观锁对并发的控制持悲观态度,它在进行任何修改前,首先会为其加锁,确保整个修改过程中不会出现冲突,从而有效的保证数据一致性。但这样的机制同时降低了系统的并发性,尤其是两个同时修改的对象本身不存在冲突的情况。同时也可能在竞争锁的时候出现死锁,所以现在很多的系统例如Kubernetes采用了乐观并发的控制方法。


乐观锁(全称Optimistic Concurrency Control,缩写OCC)假设多用户并发的事务在处理时不会彼此影响,各事务能够在不请求锁的情况下处理各自的数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。


相对于悲观锁对锁的提前控制,乐观锁相信请求之间出现冲突的概率是比较小的,在读取及更改的过程中都是不加锁的,只有在最后提交更新时才会检测冲突,因此在高并发量的系统中占有绝对优势。同样假设用户A和B要修改同一个文件,A和B会先将文件获取到本地,然后进行修改。如果A已经修改好并且将数据提交,此时B再提交,服务器端会告知B文件已经被修改,返回冲突错误。此时冲突必须由B来解决,可以将文件重新获取回来,再一次修改后提交。


乐观锁通常通过增加一个资源版本字段,来判断请求是否冲突。初始化时指定一个版本值,每次读取数据时将版本号一同读出,每次更新数据,同时也对版本号进行更新。当服务器端收到数据时,将数据中的版本号与服务器端的做对比,如果不一致,则说明数据已经被修改,返回冲突错误。


本号涉及技术和总结(20+)

可识别小程序获取电子书详细信息

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1&

推荐阅读:



温馨提示:

请搜索“ICT_Architect”“扫一扫”二维码关注公众号,点击原文链接了解更多电子书详情

640?wx_fmt=png&wxfrom=5&wx_lazy=1

求知若渴, 虚心若愚

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值