分布式锁的各种实现

locker

分布式锁简介

分布式锁目前实现大部分方式

  • 使用mysql
  • 使用redis
  • 使用zookeeper

基于此,成熟的案例有比如redisson 这种官方比较推荐的方案,为了深入研究,准备在这个项目中复现这3种形式的锁

锁的用途

分布式锁主要可以独立于服务之外,主要的目的是在集群中保证同一时刻只能由锁的持有者对资源处理,释放锁的时候需要考虑到重入对资源的影响,串行化实现中,该项目使用了spring-retry的相关功能,将多个请求共同编排,
实现全局的锁需要依赖一个第三方系统,此系统需要满足高可用、一致性比较强同时能应付高并发的请求。

锁的不同实现方式

该项目中所有的锁实现都放入了统一枚举,目前是采用的非公平锁来实现串行化执行的目的。

me.fulln.lock.enums.LockEnum

数据库锁

  • 非重入锁

数据库非重入锁,依赖于数据库主键唯一冲突,在多个请求过来的时候,让最先访问到的请求,直接插入值来拿到锁,其他的则不断重试,直到失败,如果需要释放锁的话,直接将该锁逻辑删除即可

  • 可重入锁

数据库可重入锁,需要在表新增一个标识,来区分是本身线程的重试还是其他线程的重试,只有允许该标识下的请求进入,并成功获取锁,多次进入时,会在次数上+1,
释放锁的时候,需要判断该锁的次数是否完全达到释放的次数,每次释放将该锁的次数-1,直到0

整体来说mysql数据库来做分布式锁是比较少的,大量时间耗费在数据库连接和数据传输上了,整体感还是比较重,而且mysql使用主键冲突来判断是否获取成功,实现的不算优雅,而且在集群下,由于主从同步的的时间差,容易出现误判的情况

redis锁

redis锁主要是有setnx 可进行唯一key的设置和获取,redis的setnx是redis的一个原子操作,如果key存在则set失败,如果不存在则set成功

目前大部分分布式锁的实现都是由redis进行实现的,其实现简单,吞吐量不低,但是在集群模式下,主从复制,会遇见master锁成功后节点下线,未同步锁到其他节点,导致slave节点又一次锁成功,从而产生问题

重试获取的在redission中也有很好的实践

zookeeper锁

zk锁是通过往zk中最先写入key节点,发现自己创建的节点是最小的节点,就认为这个客户端获取到了锁.其他的客户端再进行写入的时候,发现key已经存在或者在自己创建的节点的children不是最小的,就进行等待,直到下次锁被释放的时候,再去获取锁.

zk实现的锁有个问题在于分布式锁会有羊群效应,即其他所有客户端在判断自己创建的节点是不是最小的时候,大部分都算出来不是最小的.大部分客户端都获取到了和自己不相干的通知,在集群下,对service的性能影响很大,并且如果一旦同一时间有多个节点的客户端断开连接,这个时候,服务器就会像其余客户端发送大量的事件通知,也是属于比较重的一种实现

ps: 最好使用curator 来实现zk客户端连接

项目地址

https://github.com/fulln/locks

posted @ 2021-06-06 21:57  _我在清水河边  阅读(123)  评论(0编辑  收藏  举报