0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

分析3种分布式锁的设计与实现

电子工程师 来源:未知 作者:李倩 2018-11-26 11:26 次阅读

多线程情况下对共享资源的操作需要加锁,避免数据被写乱,在分布式系统中,这个问题也是存在的,此时就需要一个分布式锁服务。常见的分布式锁实现一般是基于DB、Redis、zookeeper。下面笔者会按照顺序分析下这3种分布式锁的设计与实现,想直接看分布式锁总结的小伙伴可直接翻到文档末尾处。

分布式锁的实现由多种方式,但是不管怎样,分布式锁一般要有以下特点:

排他性:任意时刻,只能有一个client能获取到锁

容错性:分布式锁服务一般要满足AP,也就是说,只要分布式锁服务集群节点大部分存活,client就可以进行加锁解锁操作

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

除了以上特点之外,分布式锁最好也能满足可重入、高性能、阻塞锁特性(AQS这种,能够及时从阻塞状态唤醒)等,下面就话不多说,赶紧上(开往分布式锁的设计与实现的)车~

DB锁

在数据库新建一张表用于控制并发控制,表结构可以如下所示:

CREATETABLE`lock_table`(`id`int(11)unsignedNOTNULLCOMMENT'主键',`key_id`bigint(20)NOTNULLCOMMENT'分布式key',`memo`varchar(43)NOTNULLDEFAULT''COMMENT'可记录操作内容',`update_time`datetimeNOTNULLCOMMENT'更新时间',PRIMARYKEY(`id`,`key_id`),UNIQUEKEY`key_id`(`key_id`)USINGBTREE)ENGINE=InnoDBDEFAULTCHARSET=utf8;

key_id作为分布式key用来并发控制,memo可用来记录一些操作内容(比如memo可用来支持重入特性,标记下当前加锁的client和加锁次数)。将key_id设置为唯一索引,保证了针对同一个key_id只有一个加锁(数据插入)能成功。此时lock和unlock伪代码如下:

deflock:execsql:insertintolock_table(key_id,memo,update_time)values(key_id,memo,NOW())ifresult==true:returntrueelse:returnfalsedefunlock:execsql:deletefromlock_tablewherekey_id='key_id'andmemo='memo'

注意,伪代码中的lock操作是非阻塞锁,也就是tryLock,如果想实现阻塞(或者阻塞超时)加锁,只修反复执行lock伪代码直到加锁成功为止即可。基于DB的分布式锁其实有一个问题,那就是如果加锁成功后,client端宕机或者由于网络原因导致没有解锁,那么其他client就无法对该key_id进行加锁并且无法释放了。为了能够让锁失效,需要在应用层加上定时任务,去删除过期还未解锁的记录,比如删除2分钟前未解锁的伪代码如下:

defclear_timeout_lock:execsql:deletefromlock_tablewhereupdate_time<  ADDTIME(NOW(),'-00:02:00')

因为单实例DB的TPS一般为几百,所以基于DB的分布式性能上限一般也是1k以下,一般在并发量不大的场景下该分布式锁是满足需求的,不会出现性能问题。不过DB作为分布式锁服务需要考虑单点问题,对于分布式系统来说是不允许出现单点的,一般通过数据库的同步复制,以及使用vip切换Master就能解决这个问题。

以上DB分布式锁是通过insert来实现的,如果加锁的数据已经在数据库中存在,那么用select xxx where key_id = xxx for udpate方式来做也是可以的。

Redis锁

Redis锁是通过以下命令对资源进行加锁:

setkey_idkey_valueNXPXexpireTime

其中,set nx命令只会在key不存在时给key进行赋值,px用来设置key过期时间,key_value一般是随机值,用来保证释放锁的安全性(释放时会判断是否是之前设置过的随机值,只有是才释放锁)。由于资源设置了过期时间,一定时间后锁会自动释放。

set nx保证并发加锁时只有一个client能设置成功(Redis内部是单线程,并且数据存在内存中,也就是说redis内部执行命令是不会有多线程同步问题的),此时的lock/unlock伪代码如下:

deflock:if(redis.call('set',KEYS[1],ARGV[1],'ex',ARGV[2],'nx'))thenreturntrueendreturnfalsedefunlock:if(redis.call('get',KEYS[1])==ARGV[1])thenredis.call('del',KEYS[1])returntrueendreturnfalse

分布式锁服务中的一个问题

如果一个获取到锁的client因为某种原因导致没能及时释放锁,并且redis因为超时释放了锁,另外一个client获取到了锁,此时情况如下图所示:

那么如何解决这个问题呢,一种方案是引入锁续约机制,也就是获取锁之后,释放锁之前,会定时进行锁续约,比如以锁超时时间的1/3为间隔周期进行锁续约。

关于开源的redis的分布式锁实现有很多,比较出名的有redisson、百度的dlock,关于分布式锁,笔者也写了一个简易版的分布式锁redis-lock,主要是增加了锁续约和可同时针对多个key加锁的机制。

对于高可用性,一般可以通过集群或者master-slave来解决,redis锁优势是性能出色,劣势就是由于数据在内存中,一旦缓存服务宕机,锁数据就丢失了。像redis自带复制功能,可以对数据可靠性有一定的保证,但是由于复制也是异步完成的,因此依然可能出现master节点写入锁数据而未同步到slave节点的时候宕机,锁数据丢失问题。

zookeeper分布式锁

ZooKeeper是一个高可用的分布式协调服务,由雅虎创建,是Google Chubby的开源实现。ZooKeeper提供了一项基本的服务:分布式锁服务。zookeeper重要的3个特征是:zab协议、node存储模型和watcher机制。通过zab协议保证数据一致性,zookeeper集群部署保证可用性,node存储在内存中,提高了数据操作性能,使用watcher机制,实现了通知机制(比如加锁成功的client释放锁时可以通知到其他client)。

zookeeper node模型支持临时节点特性,即client写入的数据时临时数据,当客户端宕机时临时数据会被删除,这样就不需要给锁增加超时释放机制了。当针对同一个path并发多个创建请求时,只有一个client能创建成功,这个特性用来实现分布式锁。注意:如果client端没有宕机,由于网络原因导致zookeeper服务与client心跳失败,那么zookeeper也会把临时数据给删除掉的,这时如果client还在操作共享数据,是有一定风险的。

基于zookeeper实现分布式锁,相对于基于redis和DB的实现来说,使用上更容易,效率与稳定性较好。curator封装了对zookeeper的api操作,同时也封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等,使用curator进行分布式加锁示例如下:

org.apache.curatorcurator-framework2.12.0 org.apache.curatorcurator-recipes2.12.0{try{lock.acquire();try{System.out.println("zookeeperacquiresuccess:"+Thread.currentThread().getName());Thread.sleep(1000);}catch(Exceptione){e.printStackTrace();}finally{lock.release();}}catch(Exceptionex){ex.printStackTrace();}};ExecutorServiceexecutor=Executors.newFixedThreadPool(10);for(inti=0;i< 1000; i++) {        executor.execute(task);    }    LockSupport.park();}

总结

从上面介绍的3种分布式锁的设计与实现中,我们可以看出每种实现都有各自的特点,针对潜在的问题有不同的解决方案,归纳如下:

性能:redis > zookeeper > db。

避免死锁:DB通过应用层设置定时任务来删除过期还未释放的锁,redis通过设置超时时间来解决,而zookeeper是通过临时节点来解决。

可用性:DB可通过数据库同步复制,vip切换master来解决,redis可通过集群或者master-slave方式来解决,zookeeper本身自己是通过zab协议集群部署来解决的。注意,DB和redis的复制一般都是异步的,也就是说某些时刻分布式锁发生故障可能存在数据不一致问题,而zookeeper本身通过zab协议保证集群内(至少n/2+1个)节点数据一致性。

锁唤醒:DB和redis分布式锁一般不支持唤醒机制(也可以通过应用层自己做轮询检测锁是否空闲,空闲就唤醒内部加锁线程),zookeeper可通过本身的watcher/notify机制来做。

使用分布式锁,安全性上和多线程(同一个进程内)加锁是没法比的,可能由于网络原因,分布式锁服务(因为超时或者认为client挂了)将加锁资源给删除了,如果client端继续操作共享资源,此时是有隐患的。因此,对于分布式锁,一个是尽量提高分布式锁服务的可用性,另一个就是要部署同一内网,尽量降低网络问题发生几率。这样来看,貌似分布式锁服务不是“完美”的(PS:威廉希尔官方网站 貌似也不好做到十全十美 :( ),那么开发人员该如何选择分布式锁呢?最好是结合自己的业务实际场景,来选择不同的分布式锁实现,一般来说,基于redis的分布式锁服务应用较多。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    3799

    浏览量

    64375
  • 多线程
    +关注

    关注

    0

    文章

    278

    浏览量

    19948

原文标题:一文弄懂“分布式锁”

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Redis 分布式的正确实现方式

    分布式一般有三实现方式:1. 数据库乐观;2. 基于Redis的分布式
    的头像 发表于 05-31 14:19 3593次阅读

    Java:Redis分布式的原理和案例

    要介绍分布式,首先要提到与分布式锁相对应的是线程、进程
    的头像 发表于 07-01 11:49 3872次阅读

    分布式的基本原理和案例实现

    前面我们有聊过乐观和悲观实现,均是对于单体架构的场景下的实现。那么现在我们来总结看下分布式情况下如何
    的头像 发表于 07-01 14:53 3334次阅读
    <b class='flag-5'>分布式</b><b class='flag-5'>锁</b>的基本原理和案例<b class='flag-5'>实现</b>

    分布式的设计与实现

    今天跟大家探讨一下分布式的设计与实现。希望对大家有帮助,如果有不正确的地方,欢迎指出,一起学习,一起进步哈。
    的头像 发表于 05-13 15:36 1754次阅读

    深入理解redis分布式

    系统不同进程共同访问共享资源的一实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 业界流行的分布式
    的头像 发表于 10-08 14:13 952次阅读
    深入理解redis<b class='flag-5'>分布式</b><b class='flag-5'>锁</b>

    Redis实现分布式的几种方案

    本文将介绍什么是分布式,以及使用Redis实现分布式的几种方案。 前言 了解分布式
    的头像 发表于 10-11 15:19 678次阅读

    什么是分布式 Redis的五分布式方案

    本地加锁的方式在分布式的场景下不适用,所以本文我们来探讨下如何引入分布式解决本地的问题。本篇所有代码和业务基于我的开源项目 PassJava。
    发表于 10-23 11:35 1187次阅读
    什么是<b class='flag-5'>分布式</b><b class='flag-5'>锁</b> Redis的五<b class='flag-5'>种</b><b class='flag-5'>分布式</b><b class='flag-5'>锁</b>方案

    redis分布式如何实现

    Redis分布式是一基于Redis实现的机制,可以用于多个进程或多台服务器之间对共享资源的并发访问控制。在分布式系统中,由于多个进程或多
    的头像 发表于 11-16 11:29 534次阅读

    redis分布式可能出现的问题

    Redis分布式是一常用的机制,用于解决多个进程或多台服务器对共享资源的并发访问问题。然而,由于分布式环境的复杂性,使用Redis
    的头像 发表于 11-16 11:40 1380次阅读

    redis分布式死锁处理方案

    引言: 随着分布式系统的广泛应用,尤其是在大规模并发操作下,对并发控制的需求越来越高。Redis分布式作为一常见的分布式
    的头像 发表于 11-16 11:44 1753次阅读

    redis分布式的应用场景有哪些

    Redis分布式是一基于Redis实现分布式机制,可以在
    的头像 发表于 12-04 11:21 1440次阅读

    redis分布式三个方法

    的三常见的分布式实现方法:基于SETNX命令的简单分布式、基于SET命令的带过期时间的
    的头像 发表于 12-04 11:22 1460次阅读

    如何实现Redis分布式

    机制,下面将详细介绍如何实现Redis分布式。 一、引言 在分布式系统中,多个节点可能同时读写同一共享资源。如果没有实现互斥访问和同步机制
    的头像 发表于 12-04 11:24 704次阅读

    浅析Redis 分布式解决方案

    Redis 分布式解决方案是一基于Redis实现分布式机制,可以确保在
    的头像 发表于 12-04 14:00 497次阅读

    分布式的三实现方式

    分布式的三实现方式  分布式是在分布式系统中用
    的头像 发表于 12-28 10:01 903次阅读