分布式锁实现

1 基本的实现方式

分布式锁的主要实现方式主要有以下几种:mysql,zookeeper和redis。下面依次介绍这三种组件实现分布式锁的方式。

2 分布式锁的几个问题

在使用分布式锁的时候,需要考虑如下几个问题:假如我们在多线程下拿锁(多线程和多进程是一样的):

  • 假如设置了锁的有效期,在有效期内线程没有执行完怎么办。
  • 假如不设置锁的有效期,线程异常挂掉,所有线程都拿不到锁了怎么办。
    带着这样的问题,看下面的实现。

3 mysql实现分布式锁

先select,如果没有则,insert一个key,insert成功则拿到锁,insert不成功则拿不到锁。如果第一次select到了,那么用select for update操作施加一个x锁,for update拿到数据则拿锁成功。
锁的有效期就是线程的有效期,线程如果中断了或者异常退出了,一般都会捕获异常,回滚事务,释放锁。

4 zookeeper实现分布式锁

zookeeper实现分布式锁是利用的zookeeper可以创建临时节点的机制,同时利用了zk的watcher机制,如果不太熟悉zk的可以看我的zk文章,一个常见的使用zk作为分布式锁的方法为:

  • 线程在zk上创建临时节点,如果创建成功,则持有节点,并拿到锁;如果创建失败,则监听这个节点。
  • 线程退出,正常退出则删除节点,异常中断则,当SESSIONEXPIRED(可设置)之后,zk会把这个会话建立的临时数据移除。同时,所有等待这个节点的线程会收到通知。
  • 收到通知的线程都去拿锁,创建临时节点,但是只有一个可以创建成功。拿到锁。

细心的读者可能发现了问题,所有的线程都去拿锁,这就是惊群效应,那么如何避免呢?zk还提供了一种创建模式,可以创建顺序节点,那么该怎么做呢?

  • 所有线程创建顺序节点,并且watch本节点的前一个节点。
  • 如果本节点的前一个节点被删除,则拿到锁。
  • 本线程做完工作则delete节点,异常退出则等待超时。

zk的实现机制很完美,有什么问题呢?
zk的事务提交流程比较长,在集群下需要非leader转发到leader,leader发起投票,过半提交后leadercommit事务,follower 提交事务。整个流程比较长,有些高并发场景下可能不太适用,而且新增了一个zk组件,不便维护。

3 redis实现分布式锁

redis的分布式锁实现,要依赖于原子操作和setnx命令。setnx拿到锁并同时设置超时时间的原子操作命令为:

SET key value [EX seconds] [PX milliseconds] [NX|XX]
实例:SET resource-name any-string NX EX max-lock-time

这个命令解决了

假如不设置锁的有效期,线程异常挂掉,所有线程都拿不到锁了怎么办。

的问题。但是续期怎么做呢。
有两种做法:
第一种:

  • 第一种是每次set key的时候set一个唯一id(可以用时间+mac+线程号)。
  • 每次释放的时候看是不是自己的锁,放置过期了被别人拿到了,又把别人的锁释放了。但是这一步需要原子操作,可以用lua脚本:
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                "return nil;" +
            "end; " +
            // 将该客户端对应的锁的 hash 结构的 value 值递减为 0 后再进行删除
            // 然后再向通道名为 redisson_lock__channel publish 一条 UNLOCK_MESSAGE 信息
            "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
            "if (counter > 0) then " +
                "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                "return 0; " +
            "else " +
                "redis.call('del', KEYS[1]); " +
                "redis.call('publish', KEYS[2], ARGV[1]); " +
                "return 1; "+
            "end; " +
            "return nil;",

第二种,使用看门狗:

  • set key的时候需要set进线程id
  • Watch Dog 机制其实就是一个后台定时任务线程,获取锁成功之后,会将持有锁的线程放入到一个 RedissonLock.EXPIRATION_RENEWAL_MAP里面,然后每隔 10 秒 (internalLockLeaseTime / 3) 检查一下,如果客户端 1 还持有锁 key(判断客户端是否还持有 key,其实就是遍历 EXPIRATION_RENEWAL_MAP 里面线程 id 然后根据线程 id 去 Redis 中查,如果存在就会延长 key 的时间),那么就会不断的延长锁 key 的生存时间。
  • 锁释放也需要lua脚本。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342