分布式锁总集

分布式锁概念

  分布式锁是控制分布式系统之间同步访问共享资源的一种方式,通过互斥来保持一致性。

  分布式锁来对分布式系统多进程访问资源进行控制,因此分布式锁是为了解决分布式互斥问题!


 

线程锁和进程锁

线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如Synchronized、Lock等

进程锁:控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁 

 

 

分布式锁应该具备哪些条件:

  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
  • 具备锁失效机制,即自动解锁,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

 

实现方式可分为2类:自旋方式和Watch监听方式

自旋方式:基于数据库和基于Redis的实现就是需要在客户端未获得锁时,进入一个循环,不断的尝试请求是否能获得锁,直到成功或者超时过期为止。

监听方式:客户端Watch监听某个key就可以了,锁可用的时候会通知客户端,客户端不需要反复请求,基于zooKeeper和基于Etcd实现分布式锁就是用这种方式。

 

具体的4种实现方式:

  1. 基于数据库
  2. 基于Redis
  3. etcd
  4. ZooKeeper

 

1)基于数据库

通过创建一条唯一记录来表示一个锁,唯一记录添加成功,锁就创建成功,释放锁的话需要删除记录,但是很容易出现性能瓶颈,因此基本上不会使用数据库作为分布式锁

缺点:

  • 数据库是单点,非常依赖数据库的可用性
  • 需要额外自己维护TTL
  • 在高并发常见下数据库读写是非常缓慢

 

2)基于Redis

正确的加锁命令

SET lKey randId EX seconds | PX milliseconds | NX | XX

正常情况下是正确的

setnx lkey lvalue expire lockKey 30

注:但是这里有个问题,虽然setnx是原子性的,但是setnx + expire就不是了,也就是说setnx和expire是分两步执行的,【加锁和超时】两个操作是分开的,如果expire执行失败了,那么锁同样得不到释放。

 

3)基于Etcd

Etcd是一个Go语言实现的非常可靠的kv存储系统,常在分布式系统中存储着关键的数据,通常应用在配置中心、服务发现与注册、分布式锁等场景。

 

4)基于Zookeeoer

ZooKeeper 的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做 Znode

优点:

  • 不需要考虑锁的过期时间,使用起来比较方便
  • watch 机制,加锁失败,可以 watch 等待锁释放,实现乐观锁

缺点:

  • 性能不如 Redis
  • 部署和运维成本高
  • 客户端与 Zookeeper 的长时间失联,锁被释放问题
posted @ 2022-08-10 09:03  方达达  阅读(10)  评论(0编辑  收藏  举报