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,就是解锁成功

相关推荐
倔强的石头_7 小时前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
jiayou642 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤2 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
初次攀爬者3 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
爱可生开源社区3 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1774 天前
《从零搭建NestJS项目》
数据库·typescript
加号34 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏4 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐4 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再4 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip