分布式锁

常用的分布式锁的实现方式

1、基于数据库的分布式锁

2、基于redis的分布式锁

3、基于zookeeper的分布式锁

分布式锁要求

1、同一个方法在同一个时间只能被一个线程执行

2、可重入锁,就是说一个线程获取锁之后可以再次读取到该锁

3、获取锁的线程不能影响后续线程获取锁

4、最好是阻塞锁,具体根据业务决定

5、高可用的加锁和释放锁

一、基于数据库的分布式锁

1、基于数据库表

可以新建一个锁表,对某个字段设置唯一性约束,成功往里面insert记录的线程获得锁,释放锁就delete该条记录

这种简单的实现会有以下几个问题:

  • 锁没有失效时间,解锁失败导致后续线程无法获取锁
  • 非阻塞的,一旦insert失败,得重新执行获取锁操作
  • 非重入的,同一个线程在获取锁之后,没释放锁之前无法再次获取锁,因为记录尚在数据库中

当然也有对应的解决方法:

  • 没有失效时间?设置一个定时任务定时清理表中数据
  • 非阻塞?搞个while循环,直到获取锁
  • 非重入?记录中加上线程信息,第二次获取锁时查询记录是否存在,有直接返回锁

2、基于数据的排他锁

例如MySQL中InnoDB存储引擎的排他锁

select * from table where id = 1 for update

一个线程执行了上面sql,事务没有提交前,其他线程会被阻塞,直到获取锁或者等待超时

要注意的是,InnoDB存储引擎在加锁的时候,如果搜索条件是索引的时候会使用行级锁,如果不是索引则会使用表级锁!!!

但是具体情况还是MYSQL来决定的,如果是一个小表,它觉得使用全表扫描的时候代价小的话,则不管是否加了索引,就会使用全表查询

这个时候就很悲剧了。。。

总结:基于数据库的分布式表实现简单,容易理解,但是对数据库的性能影响有一定的影响,在并发小的情况下可以考虑。

二、基于redis的分布式锁

不一定是redis,其他一些第三方缓存产品也可以,例如memcached,这里使用redis介绍

主要运用到redis的setnx()、expire()命令,NX意思if not exits,如果没有存在该键就允许插入,还有一个XX,有就更新,没有不做处理,memcached的是add()命令

成功插入键值对的线程表示获取锁成功,通过删除命令来释放锁,同时我们还可以设置锁过期时间,从reids2.6.12版本开始,只需通过一条命令同时设置键值对和相应的过期时间,达到原子性的原则,减少了死锁的出现。以前如果setnx()后,执行的expire()失败,就会导致锁没有自动释放功能。

还有一个注意的地方就是,锁的过期时间必须大于业务的处理时间,否则锁的意义就没有了,所以实际开发中锁的过期时间设置很重要。

总结:相对于第一种方式来说,大大减少了数据库的开销,加锁和释放锁的性能上也得到了提升,实现起来也不是很难,主要是锁的控制时间不是很靠谱。

三、基于zookeeper的分布式锁

            Zookeeper提供一个多层级的节点命名空间(节点称为znode),每个节点都用一个以斜杠(/)分隔的路径表示,而且每个节点都有父节点(根节点除外),非常类似于文件系统。例如,/foo/doo这个表示一个znode,它的父节点为/foo,父父节点为/,而/为根节点没有父节点。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。Zookeeper为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M。

            而为了保证高可用,zookeeper需要以集群形态来部署,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么zookeeper本身仍然是可用的。客户端在使用zookeeper时,需要知道集群机器列表,通过与集群中的某一台机器建立TCP连接来使用服务,客户端使用这个TCP链接来发送请求、获取结果、获取监听事件以及发送心跳包。如果这个连接异常断开了,客户端可以连接到另外的机器上。            

下面描述使用zookeeper实现分布式锁的算法流程,假设锁空间的根节点为/lock:

  1. 客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。

  2. 客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁;

  3. 执行业务代码;

  4. 完成业务流程后,删除对应的子节点释放锁。

    转载自https://blog.csdn.net/qiangcuo6087/article/details/79067136

了解了一下,zookeeper提供的API实现起分布式锁还是太复杂了,懒才能促进社会进步,嘿嘿,我们可以使用curator开源项目提供的zookeeper分布式锁。

导入包就可以愉快玩耍啦

<dependency>

  <groupId>org.apache.curator</groupId>

  <artifactId>curator-recipes</artifactId>

  <version>4.0.0</version>

</dependency>

1、首先连接zookeeper

2、创建分布式锁和其根节点目录

3、mutex.acquire()获取锁

4、写业务

5、mutex.release()释放锁

总结:实现不难,但是要有zookeeper集群,理解较难,需要对zookeeper有一定的理解。

转载于:https://my.oschina.net/u/3494913/blog/2396086

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值