Redis 如何实现分布式锁

embedded/2024/11/14 21:13:24/

课程地址

单机 Redis

naive 版

加锁:

SETNX ${lockName} ${value}	# set if not exist

如果不存在则插入成功,返回 1,加锁成功;否则返回 0,加锁失败

解锁:

DEL ${lockName}

问题1

2 个线程 A、B,线程 A 拿到了锁后,宕机了怎么办?谁来释放锁?

解决:使用自动过期机制:

加锁:

SET ${lockName} ${value} EX 10 NX	# atomic opreation

解锁:

DEL ${lockName}

问题2

在这里插入图片描述

t0 时刻实例1获取到锁,t3 时刻被自动释放(国企),同时实例2获取到锁,t4 时刻实例1再次释放(实例2的锁),实例3又获取到了锁,造成了数据混乱

解决:设置 value 为当前实例线程 id,在解锁时做判断

加锁:

SET ${localName} ${threadId} EX 10 NX

解锁(使用 lua 脚本实现原子性):

current_value = get ${lockName}
if ${threadId} == current_value thenDEL ${lockName}

问题3

在这里插入图片描述

锁被自动释放的问题仍然存在

启动一个异步线程(WatchDog),获取到锁成功后启动异步线程,每隔 x 秒对当前锁续期 y 秒,直到锁被释放停止

至此,单机版的 Redis 分布式锁就得到了成功实现

集群 Redis

主节点加锁成功后崩溃,从节点上位后没有锁的信息

在这里插入图片描述

加锁:

  • 向所有节点发送加锁请求
  • 获取到锁的数量达到节点个数半数以上认为加锁成功

解锁:

删除所有实例上的锁

在这里插入图片描述

问题4

网络延迟超过了锁的过期时间

在这里插入图片描述
加锁:

一、获取当前时间
二、按顺序依次向N个Redis实例执行加锁操作

这里的加锁操作和在单实例上执行的加锁操作一样,使用SET命令,带上NX、EX/PX选项,以及带上客户端的唯一标识。当然,如果某个Redis实例发生故障了,为了保证在这种情况下,Redlock算法能够继续运行,我们需要给加锁操作设置一个超时时间。如果客户端在和一个Redis实例请求加锁时,一直到超时都没有成功,那么此时,客户端会和下一个Redis实例继续请求加锁。加锁操作的超时时间需要远远地小于锁的有效时间,一般也就是设置为几十毫秒

三、一旦客户端完成了和所有Redis实例的加锁操作,客户端就要计算整个加锁过程的总耗时

客户端只有在满足两个条件时,才能认为是加锁成功:

  • 客户端从超过半数(大于等于 N/2+1)的Redis实例上成功获取到了锁
  • 客户端获取锁的总耗时没有超过锁的有效时间

在满足了这两个条件后,我们需要重新计算这把锁的有效时间,计算的结果是锁的最初有效时间减去客户端为获取锁的总耗时。如果锁的有效时间已经来不及完成共享数据的操作了,我们可以释放锁,以免出现还没完成共享资源操作,锁就过期了的情况。

当然,如果客户端在和所有实例执行完加锁操作后,没能同时满足这两个条件,那么,客户端就要向所有Redis节点发起释放锁的操作

在这里插入图片描述


http://www.ppmy.cn/embedded/17684.html

相关文章

Kotlin语法入门-访问和属性修饰符(5)

Kotlin语法入门-访问和属性修饰符(5) 文章目录 Kotlin语法入门-访问和属性修饰符(5)五、访问和属性修饰符1、kotlin修饰符2、internal3、默认修饰符4、open关键字开启继承并实现 五、访问和属性修饰符 1、kotlin修饰符 kotlin在常见的访问修饰符private,protected…

Java -- (part13)

一.异常 1.概述 代码出现了不正常的现象 2.分类 Throwable Error -- 错误 Exception -- 异常 a.编译时期异常:语法没有错误,调用某个方法,直接爆红(因为被调用的方法底层跑了一个编译时期异常) b.运行时期异常:语法没有错误,但是一运行就报错,RuntimeException以及…

CSS 命名规范 - BEM

CSS 命名规范 - BEM 规范化命名 CSS 的选择器按照规范命名的优点: 提高代码的 可读性 和 可维护性提高 可重用性可以有效地避免组件或模块间样式的相互污染,减少嵌套层级 BEM 格式 [prefix]-[block]__[element]--[modifier]Prefix。全局前缀&#x…

C语言例题(递归、二分查找、冒泡排序)

一、递归案例 有5个人坐在在一起,问第5个人多少岁?他说比第4个人大两岁。问第4个人岁数,他说比第3个人大两岁。问第3个人,又说比第2个人大两岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请…

顺序表 (C语言版)

顺序存储: 把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。 顺序表的特点: 能在O(1)的时间内找到第i个元素存储密度高拓展容量不方便插入,删除操作不方便 C语言中可使用&am…

树倒着打印输出

思路 先向右遍历&#xff0c;同时空格也要变多&#xff0c;那么就先prt(root->right,spacecnt) 其中space是离最左边多远,cnt是每次叠加的有多远 输出最右边端点 和 空行 再向左遍历 同样prt(root->left,spacecnt) 代码 #include <iostream> #include <st…

windows ubuntu:sed,awk,grep篇:4.执行 sed

目录 23.单行内执行多个 sed 命令 24.sed 脚本文件 25.sed 注释 26.把 sed 当做命令解释器使用 27.直接修改输入文件 23.单行内执行多个 sed 命令 第一章内已经讲过&#xff0c;单行内执行多个 sed 命令有多种方法。 1. 使用多命令选项 –e 多命令选项-e 使用方法如…

postgis源码编译安装-实操成功

依赖环境安装 sqlite3安装 https://www.sqlite.org/2024/sqlite-autoconf-3450200.tar.gz tar xvf sqlite-autoconf-3450200.tar cd sqlite-autoconf-3450200 mkdir -p /home/postgres/app/postgis/sqlite3 ./configure --prefix=/home/postgres/app/postgis/sqlite3 ma…