Redis分布式锁

文章目录

1.什么是分布式锁

在一个分布式的系统中,也会涉及到多个节点访问同一个资源的情况。此时就需要通过 锁 来互斥控制,避免线程安全的情况发生。

C++ /Linux 中的mutex锁是只在当前进程中有效,在分布式多进程的环境中就不行了。

引入分布式锁:

分布式锁也是一个/一组单独的服务器,提供"加锁"服务。

在"买票"的例子中,当一个客户端进行买票时,在操作过程中就会加锁,往redis上设置一个特殊的key-value(内容是什么不重要),完成买票操作后,再删除key-value,当其他客户端也进行买票时,尝试设置key-value发现已经存在,--》加锁失败(放弃/阻塞)

setnx命令:不存在就设置,存在就出错

2.setnx

使用setnx完成加锁,del完成解锁。

但是如果程序崩溃了,没执行到解锁逻辑怎么办?

在C++中可以用到RAI I(智能指针)来释放锁。

Java中可以 try -catch -finally (finally就是无论是否异常都会执行) 来销毁锁。

但这种做法在分布式系统中没用。

当服务器掉电--》进程异常终止--》导致redis上设置的key无人删除--》导致其他服务器无法获取到锁。

这时候很容易联想到我们之前学的 "过期时间"

3.过期时间

给set的key设置过期时间,一旦时间到,key会自动被删除。

set ex nx完成设置

setnx + expire也能设置过期时间,但这里不能使用。

因为redis的多个命令直接不能保证原子性,如果两步操作完成加锁,可能会导致一个命令 成功 ,另一个命令失败。

所以还是一条命令实现比较稳妥。

4.校验id

所谓加锁,就是给redis设置一个key-value,

解锁,就是给redis上这个key-value删除。

所以可能出现服务器1执行了加锁,服务器2执行了解锁。

----》给系统带来严重的问题。

为了解决这个问题,就要引入校验机制。

步骤:

  1. 给服务器编号,让服务器有自己的身份标识
  2. 进行加锁的时候,key对应针对被操作的资源,value存储刚才服务器的编号 (出问题后能知道是哪个服务器的问题)

后续在解锁的时候,先查询是否是自己的编号,是才执行del,不是,就失败。

通过校验,可以有效避免"误解锁"

5.lua脚本

解锁的时候

  1. 查询判定
  2. del
    这两步操作标识原子的,就可能出现问题。

比如在同一个服务器中,线程a先判定自己可以解锁,然后线程b也判定自己能解锁(前面的校验是针对不同的服务器的,在同一个服务器中,如果锁的粒度过大,可能就出现被别的线程解锁的情况),之后线程a解锁,然后如果此时,服务器2号的c,要使用锁,进行加锁,然后线程b把c的锁解了,这就出问题了。

所以说,查询判定和 del解锁 操作应该是原子的,才能避免问题。

解决措施:

  • 使用redis的事务(原子性)
  • 使用lua脚本

lua是编程语言,可以作为redis的内嵌语言,特别轻量。通过lua写的脚本逻辑,在redis上运行是原子的,相当于一条命令(尽管在脚本中写了很多命令)

6.看门狗

在加锁的时候,要给key加上过期时间。

如果是静态的过期时间:

太短的话--》业务逻辑还没执行完,就释放锁了

太长的话--》锁释放不及时。

所以就需要"动态续约 "

往往服务器也要有一个专门的线程去负责续约这件事。这个线程就叫 看门狗 (watch dog)

动态续约:初识情况下,设置一个过期时间(假设1s),在还剩300ms时(可以自己设置,这里是举例),如果当前任务还没执行完,就继续续约1s,等又到300ms时,再次续约,直到执行完。

如果服务器中途崩溃了,就没人负责续约了。锁也能在短时间内释放。

7.redlock算法

使用redis分布式锁,redis可能自身会挂掉。

进行加锁--》把key设置到主节点上。如果主节点挂了,哨兵自动把从节点升成主节点,确保刚刚的锁是可以使用的。

但是主节点和从节点间的数据同步,是存在延时的。如果主节点加锁后,还没同步给从节点就挂了,从节点晋升成为主节点,但是刚刚加锁的数据是没有的。

作为分布式系统,就要随时考虑某个节点挂了会不会影响大局。

解决措施:引入redlock算法(作者给出的另一个方案)

主要思路就是 冗余

之前都是主从结构,redlock算法把所有的节点都作为主节点。当客户端对资源进行加锁时,会按照一定的顺序,在所有的redis服务器上都进行加锁操作。

只要有一半以上的服务器加锁成功,那么客户端的加锁就成功了(其他客户端再次对该资源加锁,不可能有一半的服务器上还能加锁--》加锁不成功)

同样解锁也是一样,超过一半的key被del,就是解锁成功

相关推荐
Li zlun3 小时前
MySQL 配置管理与日志系统完全指南:从基础到高级优化
数据库·mysql
wzg20164 小时前
vscode 配置使用pyqt5
开发语言·数据库·qt
老朋友此林5 小时前
MongoDB GEO 项目场景 ms-scope 实战
java·数据库·spring boot·mongodb
极限实验室5 小时前
如何使用 INFINI Gateway 对比 ES 索引数据
数据库
Raymond运维7 小时前
MySQL包安装 -- RHEL系列(离线RPM包安装MySQL)
linux·运维·数据库·mysql
岁岁岁平安7 小时前
分布式系统相关概念(单体、集群、分布式、分布式集群、微服务)
分布式·微服务
养生技术人7 小时前
Oracle OCP认证考试题目详解082系列第45题
运维·数据库·sql·oracle·开闭原则·ocp
奥尔特星云大使8 小时前
mysql主从配置(保姆级)
数据库·mysql·主从复制
BD_Marathon8 小时前
【MySQL】SQL的分类
数据库·sql·mysql