题外话
基于数据库实现分布式锁(性能较低,并发不高的情况下可以使用)
搞一张表——lock(id,value,timeout_stamp),用id主键来互斥;
插入数据成功即加锁成功,将数据删除即解锁;
非阻塞式
![](https://img.haomeiwen.com/i15253776/de619a505d733faa.png)
阻塞式
mysql支持的客户端连接,峰值(300-700)
![](https://img.haomeiwen.com/i15253776/5773631a454f9870.png)
这里的sleep(10)会导致并发加锁操作的业务性能问题(解锁无通知),如果睡眠时间过短或while(true)会导致数据库访问性能问题;
无法优雅的实现阻塞式加锁
![](https://img.haomeiwen.com/i15253776/9fcc32ee41920350.png)
解锁
![](https://img.haomeiwen.com/i15253776/1b1a3bd310ebc8e4.png)
存在的问题
1、lock的id固定写死不合理;
应用不同的id来代表不同的竞争资源;
2、未考虑死锁问题,某客户端加锁成功,操作共享资源,还没有来得及释放锁,客户端宕机,当前id对应的全局lock无法释放;
应该用timeout_stamp字段记录当前锁超时的时间戳,每个客户端在尝试获取锁之前,应该先删除超时的锁
2、谁加的锁应该由谁来释放,避免锁超时导致的线程安全问题,可以用value存UUID,解锁(delete)时用条件判断,不能把别人刚刚加的锁;
主要是考虑到某个客户端(A)在机端情况下,没有在设定的超时时间内完成数据操作,其他客户端已经以超时结果认定并删除锁,这时A完成操作,将其他客户端刚刚创建的锁给删除了,所以解锁delete操作需要判断value值,谁加的锁谁来解除(仅是解锁时判断)。
仍然存在的问题
未考虑锁续命的问题,其他客户端可能会由于超时设定误删正常客户端正在使用的锁,导致线程安全问题。
解决思路分析:压力测试,覆盖性测试,选择最大完成时间+1作为当前id对应资源的锁的超时时间;日志补偿,告警,人为干预;
ZK实现分布式锁的正确姿势
原理分析
1、在对应的lock节点下创建临时顺序节点;
临时节点,在zk客户端宕机,zk会自动检测,移除节点;
2、获取lock下的所有子节点,如果发现自己的是最小的,则认为当前客户端获取到了锁。并且在使用完锁后,需要删除自己创建的临时顺序节点(不能删除别人的);
3、如果发现自己不是最小的,说明自己没有获取锁,此时客户端需要找到比自己小1号的那个节点,同时对其注册事件监听器,监听删除事件;
![](https://img.haomeiwen.com/i15253776/e7e12903abb3e286.png)
如上实现的是公平锁。
图解
![](https://img.haomeiwen.com/i15253776/2f1239dcd8601788.png)
开胃菜
zk监听节点变动,代码演示
![](https://img.haomeiwen.com/i15253776/bca58c01f9d4a5ba.png)
Thread.currentThread().join();不让@Test单元测试线程退出;
正确的使用zk分布式锁的姿势
原生用法
![](https://img.haomeiwen.com/i15253776/45e6599346169c10.png)
![](https://img.haomeiwen.com/i15253776/519c6bd51f7e6650.png)
![](https://img.haomeiwen.com/i15253776/fbf20cb14b12120d.png)
![](https://img.haomeiwen.com/i15253776/21bbeb124f97fd0c.png)
![](https://img.haomeiwen.com/i15253776/0feaa32d96b900e3.png)
见:https://blog.csdn.net/qq_31142553/article/details/85463399
Curator封装较好,基于它来实现
![](https://img.haomeiwen.com/i15253776/d6a4fc4601d82feb.png)
使用场景
可以用来解决缓存击穿的问题
网友评论