lock是乐观锁吗_基于Zookeeper实现搜索引擎ElasticSearch分布式锁技术方案介绍

关于ElasticSearch锁:

说起ElasticSearch锁就得说一下乐观锁与悲观锁的区别以及优缺点:

乐观锁和悲观锁都是指对待并发控制的两种思想,共享锁(S锁,也叫读锁),排他锁(X锁,又称写锁),行锁,表锁,全局锁,文档锁是具体锁的实现,且都属于悲观锁,乐观锁没有锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,如果发现版本version与自己不相同,那就说明数据是已经被修改过的,那么它会重新去es中读取最新的数据版本,然后再进行数据上的操作。

悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,上锁之后就只有一个线程可以操作这条数据了,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁和悲观锁对比:

悲观锁

优点:方便,直接加锁,对应用程序来说比较透明,不需要额外的操作;

缺点:并发能力低,同一时间只能有一条线程操作数据;

乐观锁

优点:并发能力高,不给数据加锁,可大量线程并发操作;

缺点:麻烦,每次更新的时候,都要先比对版本号,然后可能需要重新加载数据,再次修改,再次更改……这个过程可能需要重复多次;

ElasticSearch锁属于乐观锁,这样在高并发的情况下就会出现问题,比方线程A在对文档id1进行更新的时候线程B过来读,根据业务需求应该B读到的是线程A更新之后的doc,但是在高并发情况下读到的可能还是A更新之前的,这样就会有数据显示问题。基于以上业务问题提出了使用分布式锁的解决方案,当然有人提议让使用版本号进行控制,不管是使用内部版本号或者自己维护外部版本号都可以,因为业务需要数据批量更新,单独维护一个documentID的版本不是很方便所以选择分布式锁方案。

当然讲到分布式锁,业界一般有两种解决方案,一种是Redis分布锁,一种是zookeeper分布锁。

对于redis分布锁而言,它有以下缺点:

  • 它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能。
  • 另外来说的话,Redis 的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题。锁的模型不够健壮。
  • 即便使用 Redlock 算法来实现,在某些复杂场景下,也无法保证其实现 100% 没有问题。
  • Redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。

但是另一方面使用redis实现分布式锁在很多企业中非常常见,而且大部分情况下都不会遇到所谓的“极端复杂场景”

所以使用redis作为分布式锁也不失为一种好的方案,最重要一点是Redis的性能很高,可以支撑高并发的获取、释放锁操作。

对于ZooKeeper分布式锁而言:

  • ZK 天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。
  • 如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。

但是ZK也有其缺点:如果有较多的客户端频繁的申请加锁、释放锁,对于ZK集群的压力会比较大。

基于Redis锁有可能存在隐患,可能导致数据不对,而且公司有ZK集群,所以选择ZK实现分布式锁。

ZooKeeper有四种节点类型:

1、持久化节点 :所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点——不会因为创建该节点的客户端会话失效而消失。

2、持久化顺序节点:这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。基于持久顺序节点原理的经典应用-分布式唯一ID生成器。

3、临时节点:和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点,集群zk环境下,同一个路径的临时节点只能成功创建一个,利用这个特性可以用来实现master-slave选举。

4、临时顺序节点:相对于临时节点而言,临时顺序节点比临时节点多了个有序,也就是说,没创建一个节点都会加上节点对应的序号,先创建成功,序号越小。其经典应用为实现分布式锁。

监视器(watcher):

当zookeeper创建一个节点时,会注册一个该节点的监视器,当节点状态发生改变时,watch会被触发,zookeeper将会向客户端发送一条通知(就一条,因为watch只能被触发一次)。

原理:

如果使用ZK原生方式来实现的话还是比较复杂的,基于这种场景,为了避免重复造轮子,我们利用Apache的开源客户端Curator来实现分布式锁。Curator内部是通过InterProcessMutex(可重入锁)来在zookeeper中创建临时有序节点实现的,如果通过临时节点及watch机制实现锁的话,这种方式存在一个比较大的问题:所有取锁失败的进程都在等待、监听创建的节点释放,很容易发生"羊群效应",zookeeper的压力是比较大的,而临时有序节点就很好的避免了这个问题,Curator内部就是创建的临时有序节点。

ZK实现分布式锁的算法流程如下:

1、客户端连接ZK,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点我/lock/lock-0000000000,第二个为 /lock/lock-0000000001

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

3、执行业务代码

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

注解:本来想着今天更新ES搜索引擎系列(二),但是本着技术是为了解决实际问题的原则,所以讲解了使用ES引擎下使用分布式锁场。

4ec181e4fc1689579cf41aca02476767.png

醉里挑灯看剑,梦中吹角连营。八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。

马作的卢飞快,弓如霹雳弦惊。了却君王天下事,赢得身前身后名,可怜白发生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值