分布式锁实现方案

01 背景

    在单机系统中,多个线程同时访问某个共享资源时,可以采用线程间加锁保证数据一致性。但是,在分布式系统中,程序运行在多台机器上,各个节点之间无从知晓共享资源的锁定状态,即这种共享资源已经不是线程级别的,而是进程之间的。

    此时,就需要引入分布式锁,以实现多个客户端互斥访问共享资源。

    需要加锁的场景需要满足以下条件:

    1、共享资源;

    2、共享资源互斥;

    3、多任务环境。

    分布式锁的思路是:在系统中提供一个全局唯一的针对共享资源获取锁的组件,系统中需要访问共享资源时,都向该组件申请锁,待使用完毕后,释放锁。

02 特点

    分布式锁一般要有以下特点:

    排他性:任意时刻,只能有一个客户端能获取到锁。

    容错性:分布式锁服务一般要满足AP(即可用性Availability、分区容错性Partition tolerance,这与分布式事务强调一致性有区别),也就是说,只要分布式锁服务集群节点大部分存活,客户端就可以进行加锁解锁操作。

    避免死锁:分布式锁一定能得到释放,即使客户端在释放之前崩溃或者网络不可达。

03 方案

    针对分布式锁的实现,目前比较常用的方案:

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

    2、 基于缓存(redis)实现分布式锁

    3、 基于Zookeeper实现分布式锁

04 DB锁

4.1 实现

    1、唯一约束

    2、基于数据库来做分布式锁的话,通常有两种做法:

    基于数据库的乐观锁(lock in share mode)

    基于数据库的悲观锁(select ... for update)

4.2 特点

    优点:

    实现简单    

    缺点:

    1、可用性差(锁的可用性依赖于数据库,如果数据库故障,则系统不可用);

    2、数据库性能存在瓶颈,不适合高并发场景;

    3、锁的失效时间难以控制,删除锁失败容易导致死锁。即这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。

    说明:一般在分布式系统中使用这种机制实现分布式锁时,需要业务侧增加控制锁超时和重试的流程。

05 Redis分布式锁

5.1 实现

   加锁和解锁的锁必须是同一个,常见的解决方案是给每个锁一个钥匙(唯一ID),加锁时生成,解锁时判断。

    1、redis原子操作

    基于Redis实现的锁机制,主要是依赖redis自身的原子操作。

    加锁

    setnx命令加锁,并设置锁的有效时间和持有人标识:

    SET user_key user_value NX PX 100

    参数:

    NX:只在在键不存在时,才对键进行设置操作,SET key value NX 效果等同于 SETNX key value

    PX millisecond:设置键的过期时间为millisecond毫秒,当超过这个时间后,设置的键会自动失效

    解锁

    检查是否持有锁,然后删除锁:

    delete values命令删除锁

    value具有唯一性,这是避免了一种情况:假设A获取了锁,过期时间200ms,此时250ms之后,锁已经自动释放了,A去释放锁,但是此时可能B获取了锁。A客户端就不能删除B的锁了。

    2、Redlock

    使用redis做分布式锁的缺点在于:如果采用单机部署模式,会存在单点问题,只要redis故障了。加锁就不行了。

    基于以上的考虑,其实redis的作者也考虑到这个问题,他提出了一个RedLock的算法,这个算法的意思大概是这样的:

    Redlock的实现如下:

    1)获取当前时间。

    2)依次获取N个节点的锁

    3)判断是否获取锁成功。

    如果client在上述步骤中获取到了(N/2 + 1)个节点锁,并且每个锁的过期时间都是大于0的,则获取锁成功,否则失败。失败时释放锁。

    4)释放锁。

    对所有节点发送释放锁的指令,之所以要对所有节点操作?因为分布式场景下从一个节点获取锁失败不代表在那个节点上加锁失败,可能实际上加锁已经成功了,但是返回时因为网络抖动超时了。

    3、Redisson

    Redisson是一个企业级的开源Redis Client,也提供了分布式锁的支持。

5.2 特点

   优点:

    性能好,实现起来较为方便。

    缺点:

    1、单点问题。这里的单点指的是单master,就算是个集群,如果加锁成功后,锁从master复制到slave的时候挂了,也是会出现同一资源被多个client加锁的。

    2、执行时间超过了锁的过期时间。它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能。

    3、redis的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题,不够健壮。即便使用redlock算法来实现,在某些复杂场景下,也无法保证其实现100%没有问题。

06 zookeeper分布式锁

6.1 方案

    当某客户端要进行逻辑的加锁时,就在zookeeper上的某个指定节点的目录(locker目录)下生成一个唯一的临时有序节点(locker/node_N),然后判断自己是否是这些有序节点中序号最小的一个,如果是,则算是获取了锁。如果不是,则说明没有获取到锁,那么就需要在序列中找到比自己小的那个节点,并对其调用exist()方法,对其注册事件监听,当监听到这个节点被删除了,那就再去判断一次自己当初创建的节点是否变成了序列中最小的。如果是,则获取锁,如果不是,则重复上述步骤。

    当释放锁的时候,只需将这个临时节点删除即可。

6.2 特点

    优点:

    有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。

  缺点:

    性能上不如使用缓存实现分布式锁,如果有较多的客户端频繁的申请加锁、释放锁,对于zk集群的压力会比较大。

07 选择

    具体选择哪种分布式锁实现方案,需要结合业务场景,结合对性能、可靠性、复杂性的要求,具体如下:

    1、从理解的难易程度角度(从低到高)

    数据库 > 缓存 > Zookeeper

    2、从实现的复杂性角度(从低到高)

    Zookeeper >= 缓存 > 数据库

    3、从性能角度(从高到低)

    缓存 > Zookeeper >= 数据库

    4、从可靠性角度(从高到低)

    Zookeeper > 缓存 > 数据库

    综上所述:

    如果系统不想引入过多网元,可以采用数据库锁实现,好处就是比较容易理解,但是这种方案业务层控制逻辑多且复杂,需要对业务侧足够了解,易于理解但是实现复杂度最高。

    如果追求高性能,Redis是最佳选择,但是redis是有可能存在隐患的,可能会导致数据不对的情况,可靠性不如ZK。

    如果系统已经存在ZK集群,优先选用ZK实现,实现最简单,且可以提供高可靠性,性能稍逊Redis缓存方案。

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

推荐阅读更多精彩内容