分布式锁

一、
分布式锁,的原理,都是,找一个各个服务都能访问的中间层,然后,通过获得这个中间层的占有权达到锁的效果。

1.1、redis分布式锁
redis锁有一个工具包 redisson
里面包装好了,redis锁相关的各种操作,包括可重入锁、公平锁、联锁、红锁、 读写锁等等,这个待会再说。

1.1.1 如果我们设计,会怎么做。

redis锁的大概原理分析如下:

while(true){
if(redis.setnx(key)){
        // 加锁成功
      ,,,处理逻辑
    // 释放锁
      redis.delete(key);
      break;
} else{
    Thread.sleep(10);

}
}

缺点:这种方式,一旦加锁的服务挂了,setnx成功,redis.delete(key)永远得不到执行,那么这个锁永远存在,其他服务获取不到锁了。

解决方式:setnx加超时机制,但是因为setnx本身没有超时机制,所以需要分步加。

while(true){
    if(redis.setnx(key,value)){
        // 30秒过期
        redis.expire(key, 30秒);
        // 加锁成功
        ,,,处理逻辑
        // 释放锁
         redis.delete(key);
        break;
} else{
    Thread.sleep(10);
}
}

可是这样又有一个问题,如果setnx成功,还没来的急expire,服务就挂了,依然会有锁永远无法释放的问题。

那么假设,加上multi,使得setnx和expire一起执行,那么就不会存在setnx执行后,没有执行expire的问题。但是,multi并非是完全事务的,也就是说,setnx失败,expire还是会执行。所以,可能会造成,key一直被expire刷新过期时间。

而在redis 2.6.12版本之后,set,具有了和setnx一样的功能:
set key value NX PX 30000 :nx是setnx的意思,px是过期时间的意思,30000毫秒过期。
这样,就能保证set和expire原子性了。

但是,这样又会有新的问题,因为有了过期时间,如果一个线程加锁后,执行业务逻辑时间太长,锁超过了30秒过期时间,锁已经过期了,并且已经被别的线程加锁了,然后这个旧线程delete了别人的锁。

解决方案:

1、可以在服务端,加一个延迟队列,比如ScheduledThreadPoolExecutor线程池,延迟10秒,expire刷新一下过期时间,这样,一旦服务挂了,这个延迟线程池也得不到执行了。

2、set key value的时候,value弄一个uuid,唯一标识符。然后delete的时候,进行对比,相同,则删除,不相同,则回滚,报错什么的。
但是,问题是,这种机制,get value和对比value,应该保证原子性,否则,get value之后,删除value之前,value过期了,并且,被别人加锁了,那么删除value还是会删除别人的锁。
解决方案:eval执行lua脚本,保证get和对比value的原子性。
redis提供了通过lua脚本自定义命令的功能,比如:

set foo bar
eval 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end' 1 foo bar

如此,基本上是能做到分布式的功能。但是,一旦redis挂了,或者如果是主从结构的redis,master节点挂了,锁还没有同步到从节点,然后从节点就哨兵选举成master节点,就会导致有之间锁的线程,错误。
我个人觉得:上面的redis 节点挂了,导致锁的失败影响是很小的。只会影响到一个锁线程,而且,如果value是uuid的话,那个锁线程如果执行完之后,delete节点的时候,发现uuid对不上,还会回滚。如果是延迟expire刷新过期时间的话,确实会造成,两个线程的delete别人的锁。

1.1.2 redis redlock
redis红锁,的大概思想是,在redis cluster环境下,轮流在多个master节点上建立锁,只要当建立的锁的数量大于master节点数量的一半以上,才算建立成功,这样,一旦一个节点挂了,其他线程,也不可能再在一半master节点以上建立锁成功。
建立的锁也是有失效期的,而且,建立锁时有超时机制,在对一个节点建立锁时间太长了,则超时失败,再另一个节点上建锁。
如果建立失败,则依此删除这个锁。

1.1.3 redission的lock
上面说的,redis锁,直接轮询争抢锁的话,会有一个什么问题呢?
如果很多线程并发轮询,而获取锁的线程,迟迟不释放,则会造成cpu负载过高。
redission的lock的实现机制是,eval lua脚本,然后通过redis的pub/sub发布订阅,加上jdk的locksupport.parkNanos()锁线程的方式,解决了轮旋的方式。

总结:redis 锁,很多人都不建议在生产环境使用,比如上面说的单点redis的问题,集群的red lock 也会有时钟漂移等各种问题。
但我个人觉得,使用redission基本上能够很好的搞定分布式锁。

二、zookeeper锁

zookeeper锁

zookeeper的监听回调结点的删除创建机制可以实现分布式锁的功能,大概原理就是加锁,则看是否锁节点存在,存在则说明别人已经获得锁了,则添加一个节点删除监听,然后,等锁释放后,回去回调这个监听,然后重新获得锁。
释放锁,则删除这个节点。

具体代码,这里写的很好:https://www.jianshu.com/p/5d12a01018e1

如果我们使用zookeeper锁,还是用工具类好,比如Curator,可以参考:
https://www.jianshu.com/p/31335efec309

三、zookeeper锁和redis锁对比
redis分布式锁,轮询获取锁,比较消耗性能,zk分布式锁,监听回调机制,性能开销较小

zookeeper不会存在,服务挂了,锁永远存在的线程,zookeeper可以创建临时节点,zookeeper感应到服务挂了,会自己删除锁节点。

三、数据库实现分布式锁
mysql的for update可以实现锁,但是吞吐量太低了,不考虑。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容