分布式锁

1 什么是分布式锁?

分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。相对应的是线程锁、进程锁。

线程锁(单体同一个数据库):主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。

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

分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。

 

2 为什么要用分布式锁?

答:为分两种情况:

2.1 共享锁:进程A和进程B同时访问资源,读操作。幂等性避免重复加锁。

2.2 独占锁:进程A和进程B同时访问分布式锁,写操作。非幂等性同一时刻只允许一个线程或进程访问这个共享资源。

注:幂等性

  • 所谓幂等性通俗的将就是一次请求和多次请求同一个资源产生相同的副作用。用数学语言表达就是f(x)=f(f(x))
  • 维基百科的幂等性定义如下:

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的,更复杂的操作幂等保证是利用唯一交易号(流水号)实现.

 

 

 

 

 

3 分布式锁的实现方式

基于数据库实现分布式锁,基于Zookeeper实现分布式锁,基于reids实现分布式锁

3.1 基于数据库实现分布式锁

使用专用的数据表:

适用场景:没有其他中间件可以使用,需要加锁的资源恰好有对应的数据表

优点:理解起来简单,不需要维护其他中间件

缺点:需要自己实现加锁/解锁过程,性能较差

 

3.2 基于数据库实现分布式锁基于Zookeeper实现分布式锁(CP)

https://blog.csdn.net/xiaolinzi176/article/details/126193143

1)创建临时有序节点 - 按顺序创建节点,最小节点表示获取锁。

线程去创建/resource_name子节点时会自动编号,第一个编号是/0000001。

第一个线程去创建锁成功并且发现编号是/0000001并且是最小编号,那就直接保留执行程序;

第二个线程再去获取锁时,创建的子节点会自动编号为/0000002,该线程会发现这个节点不是最小节点,就向上一个节点/0000001设置一个watcher监视器,待/0000001线程执行完毕释放的时候就直接触发/0000002执行程序;

第三个个线程再去获取锁时,创建的子节点会自动编号为/0000003,该线程会发现这个节点不是最小节点,就向上一个节点/000000x设置一个watcher监视器,待/000000x线程执行完毕释放的时候就直接触发/0000003执行程序;

设置watcher监视器原生代码对节点增,删,改操作:https://blog.csdn.net/yeshu2780/article/details/113996365

 

 

 

 

 

 

解锁流程:

  • 进行重入的判断(利用ThreadLocal)
    • 若为重入,在重入次数减1,返回
  • 删除zookeeper上的有序节点

 

3.3 基于reids实现分布式锁 (AP)

redis(remote dictionary server)是一个k-v存储中间件。

两种方式加锁:

1)基于SET NX (SET key value NX PX milliseconds) - 通过0和1去判断是否获取得到锁。

 

 

2)基于Ridession - 提供分布式锁的方式,调用里面封装好的api(lock和unlock)。用Lua脚本实现加锁和解锁。还提供一个watchdoc(看门狗)监控锁的状态,每10s对key进行续约。

关键点:

a. 对key不设置过期时间,由Redisson在加锁成功后给维护一个watchdog看门狗,watchdog负责定时监听并处理,在锁没有被释放且快要过期的时候自动对锁进行续期,保证解锁前锁不会自动失效
b. 通过Lua脚本实现了加锁和解锁的原子操作
c. 通过记录获取锁的客户端id,每次加锁时判断是否是当前客户端已经获得锁,实现了可重入锁。

redis分布式锁实现原理图

 

 加锁后开启一个守护线程进行监听"看门狗"。Redisson超时时间默认设置30s,线程每10s调用一次判断锁还是否存在,如果存在则延长锁的超时时间。

 

 

 

资源:

https://www.cnblogs.com/Mufasa/p/15978320.html

https://blog.csdn.net/qq_42764269/article/details/122412431

 

posted @ 2022-11-22 19:52  NingShare  阅读(170)  评论(0编辑  收藏  举报