您的位置 首页 php

分布式锁的实现方式

老规矩福利一波!!!!!!!

面试过程中,你说你做过微服务,那么分布式事务锁的实现是必问套餐,看到一篇文章总结得比较好,思路很清晰,这里记录一下方便日后查阅和跟踪。

分布式锁三种实现方式:

1、基于数据库实现分布式锁;

2、基于缓存(Redis等)实现分布式锁;

3、基于Zookeeper实现分布式锁。

从性能角度(从高到低)来看:“缓存方式 > Zookeeper方式 >= 数据库方式”。

从可靠性角度(从高到低)来看:”Zookeeper > 缓存 > 数据库”。

下面基于这三种实现方式展开阐述。

基于数据库实现分布式锁

  • 悲观锁

利用select … where … for update 排他锁

注意 :这里需要注意的是“where name=lock ”,name字段必须要走索引, 这里是开发者最容易忽视的点 ,否则会”锁表”。因为有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。

  • “乐观锁”

通过增加递增的版本号字段实现乐观锁。

所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有 update version 失败后才能觉察到。我们的抢购、秒杀就是用了这种实现以防止超卖。

基于缓存(Redis等)实现分布式锁

通过setenx命令配合delete实现。

  • 使用介绍

SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,返回0,则什么都不做。

expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

delete

delete key:删除key

  • 实现思想

1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

  • 死锁问题

考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效地释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。

解决办法之一:设置 有效时间 的同时,在执行setnx时,把value值利用起来,设置为当前的时间,在每次获取锁失败后都判断一次当前时间和redis的缓存值时间,是否超过设置的超时时间,超过则删除此key,重新获取锁。

使用redis做分布式锁时,大家都知道setnx容易发生死锁情况,这里推荐使用 redission 来实现。

基于Zookeeper实现分布式锁

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己更小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端, Curator 提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

总结

内容有参考,有自己的思考和总结,修行之路源于记录和不断地学习。

文章来源:智云一二三科技

文章标题:分布式锁的实现方式

文章地址:https://www.zhihuclub.com/153294.shtml

关于作者: 智云科技

热门文章

网站地图