美文网首页
分布式锁

分布式锁

作者: 明翼 | 来源:发表于2020-01-19 17:44 被阅读0次

一 锁的理解

作为一个编程人员,一般对锁都比较熟悉。锁,在生活中就是控制对一个资源的独特占有权,比如我用一把锁锁上自行车,这辆自行车在锁上期间是完全属于我的,因为只有我有这个锁的钥匙;当然如果我锁上自行车,我也没办法骑车了:),也就是说现实中的锁只是自己不使用的时候,一种独特的防止别人来用的手段,是一种防止共享的手段。

计算机中锁只是借助这种独特的占有权的思想(算是临时占有权),和现实中相反的是,锁是为了共享,是为了更好地共享,解决共享冲突而采用的手段;在现实生活中,上锁了,任何人无法使用了;计算中上了锁,只能自己使用了,用完之后,释放了锁,其他使用者才可以访问。

由于计算机发展中多核,多cpu的出现,或者因为一些IO密集型程序,需要等待IO操作,如果需要提高应用的整体性能,只能提高并发性,比如增加线程,增加进程等。但是有些资源确实需要共享的,比如售票系统或电子商务系统,票数或待售物品。这样就带来了,如何控制多个执行体同一时刻访问资源的问题,以免发生冲突。

二 锁的分类

锁有很多种分类方法,有悲观锁,乐观锁;数据库中的锁有行级锁,表级锁;悲观锁,就是认为任务资源大部分时间是被别人占用的,所以每次访问均需要加锁,如果加锁不成功要么等待,要么报错,使用完成后,释放锁别人才可以使用;乐观锁就是乐观地认为需要访问的资源是自己独占的,每次直接操作,但是访问控制还是要做的,如何做那,这里面一般通过版本号来控制,比如数据库中的多版本,solr查询中的多版本。

举个乐观锁的例子,比如我们在更新solr的索引中的字段的时候,可以带上:version字段信息:

1. 如果_version_<0  如果文档存在,则拒绝更新,不存在就添加。
2.如果_version_= 0 如果文档存在,则更新,不存在则添加。
3.如果_version_ =1 如果文档存在,则更新;不存在则拒绝更新。
4.如果_version_ > 1 如果文档的版本号和verison一致,则更新,不一致则拒绝。[这里面就用乐观锁的机制,通过版本号来解决并发冲突问题]

在Java中synchronized就是重量级锁,是悲观锁;Java中通过CAS思想实现的类都是采用乐观锁的机制,比如java.util.concurrent.atomaic类。

三 分布式锁

在编程中,Java中最常用的锁比如synchronized等,ReentrantLock 等锁,只适合单JVM环境,没办法跨JVM共享的,所以在刚才提到的大型的售票系统,或电子商务中等高并发系统中,需要分布式锁来控制共享资源的访问。

3.1 数据库实现的分布式锁

最简单的实现,是通过select for update,这种方法锁住了当前数据,防止其他并发进程来操作。
好处:解决了分布式锁的问题;
坏处:
1)这种锁会一直锁住数据,直到事务提交时候才会释放锁。
2)需要设置事务的隔离级别为read commited,如果设置到其他高并发的级别,仍然会其他线程读到未提交数据,从而导致超卖的可能。
3)如果事务的操作时间太长,长时间不释放,则容易打满数据库连接;特别是事务操作中,还有第三方接口调用的时候。
4)容易产生交叉死锁,在业务加锁的时候要控制下加表的顺序,如果控制不好就会产生交叉死锁的问题。

3.2 使用zooKeeper实现分布式锁

作为分布式协调系统,zooKeeper支持四种节点类型:
1.持久性节点。
2.持久性顺序节点。
3.临时节点。
4.临时性顺序节点。
所谓临时节点,是通过客户端心跳来控制的,如果客户端挂了,则超过一定时间没有心跳连接,zookeeper就会把这个临时节点删除掉,防止客户端突然挂掉而导致锁一直无法释放的问题。

我曾经利用zk来实现一个锁,来解决多个程序同时创建solr的collection问题,利用的是临时节点,实现原理很简单:
1)每个入solr索引的进程再查询到solr的collection不存在的时候,会再特定的目录下创建临时节点。
2)如果多个进程同时创建,肯定存在创建成功和失败的情况,创建成功的就称为leader,负责collection的新建。
3)创建节点失败的则进行循环等待solr的collection的结果创建成功。

但是一般情况是比我这种复杂,比如电子商务中扣库存的例子,多个客户端同时发起扣库存请求。不光需要控制只能有一个进行资源操作,还需要控制访问的顺序,这时候就不能用这种简单的临时节点,可以用临时顺序节点。
步骤如下:

  1. 每个应用同时在一个特定的节点比如/app下去创建临时顺序节点。
  2. 每个应用对自己的前一个节点建立监听(如果有前一个节点的话)。
  3. 如果没有比自己小的,则自己获取锁,可以进行资源操作。
  4. 有比他小序号的应用,则这个应用进入等待。
  5. 一个应用删除临时顺序节点后,监听它的节点,则自动获取锁,操作资源。
  6. 特殊情况:假如A节点顺序号最小,B其次,C最大,这时候B监听A节点,如果B突然挂掉了,C发现B挂掉了就会对上监听A节点,如果AB都同时挂掉了,由于是临时节点,所以它们都会被ZK清理,所以也没关系。

用这种方式进行集群的leader选举也是极好的,只是ZK在存在大量节点监听的时候,会存在不稳定的问题。
zk方式实现的分布式锁,具有很好的性能,而且zk一般是集群部署,可靠性还是不错。

3.3 Redis实现的分布式锁

redis 也是实现分布式锁的常用手段,redis通常利用setnx来实现分布式锁,key设置为锁的id,value设置为当前时间+超时时间,或者value为线程id,同时设置超时时间;进程获取锁后,如果在超时时间内没有释放锁,锁会自动释放,这样可以避免锁一直无法释放的问题。
setnx 有0或1两个返回值。
返回1: 说明该进程获取了锁,该进程的设置成功。
返回0: 说明其他进程已经获取到了锁,进程不能访问共享资源,只能等待。
这里面有个问题,就是如果在超时的时间范围内,线程还没有使用完共享资源,比如线程A没有完成业务操作,由于超时时间已到,锁被释放了,这时候另外一个线程B意外的获取到了锁。然后A线程执行完业务的时候,就用del命令删除了B线程创建的锁,然后线程C再次获取锁成功,从而造成业务的错乱的问题。
锁的超时时间,如果设置过长了,如果一个拥有锁的应用挂了,则其他应用很长时间都获取不了这个线程。对于这种情况,有个解决办法是在加锁的应用中,增加个守护线程,守护线程定时判断主业务操作资源是否结束,如果没有结束,而超时时间就要结束了,则增加这个key的超时时间,从而达到对锁的续命的目的。

redis实现分布式锁,因为数据是放在内存中的,性能也不错;但是如果是单机的redis则存在着不可靠的问题。

redis锁我只是有所了解,自己没有亲自尝试过,需要使用这种分布式锁的朋友,还要多加验证。

相关文章

  • 分布式锁

    为什么要用分布式锁 数据库乐观锁redis分布式锁zookeeper分布式锁 使用分布式锁的场景 实现分布式锁的方...

  • 什么是分布式锁?几种分布式锁分别是怎么实现的?

    一、什么是分布式锁: 1、什么是分布式锁: 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资...

  • 4:Redis 分布式锁 (文末有项目连接)

    1:什么是缓存分布式锁 2:分布式锁的关键代码 3:业务代码使用分布式缓存锁 4:业务代码使用分布式缓存锁 5:测...

  • 锁(2)-- 分布式锁

    前言: 锁分3种:java锁、分布式锁、DB锁 分布式锁的几种实现方式 目前几乎很多大型网站及应用都是分布式部署...

  • java锁的概念

    参考文档探究分布式并发锁并发编程-锁的发展和主流分布式锁比较总结从构建分布式秒杀系统聊聊分布式锁探索并发编程(六)...

  • Redis实现分布式锁

    分布式下的分布式锁一般实现有三种: 基于数据库的乐观锁 基于redis的分布式锁 基于zookeeper的分布式锁...

  • 分布式锁

    为什么要用分布式锁? 分布式锁是悲观锁的实现; 如果采用乐观锁的方案就用不着分布式锁了。 能用乐观锁的地方尽量用乐...

  • 3.10:分布式锁

    本文将梳理微服务架构下,分布式锁的常用方案。整体包含以下三部分: 分布式锁的提出 分布式锁主流方案 分布式锁选择 ...

  • Redis实现分布式锁

    1. 分布式锁分类 数据库乐观锁 基于Redis的分布式锁 基于ZooKeeper的分布式锁 2. 组件依赖 po...

  • 大佬浅谈分布式锁

    redis 实现 redis 分布锁一、redis 实现分布式锁(可重入锁)redission 实现分布式锁1、对...

网友评论

      本文标题:分布式锁

      本文链接:https://www.haomeiwen.com/subject/gqbrzctx.html