当前位置: 首页 > news >正文

Redis——分布式锁

分布式锁

线程之间资源共享,天然就可以操作同一把锁,所以多线程之间使用锁来确保并发安全是比较容易的。而进程与进程之间的并发安全问题就比较复杂,进程之间相互独立,需要使用进程间通信机制来让不同的进程看到同一把锁。

在分布式结构中,需要让不同主机上的进程之间并发安全,其实就相当于让进程与进程之间的并发安全,只不过使用了“网络通信”这种特殊的通信方式来让不同主机上的进程看到同一把锁。在分布式锁中,锁不是一个变量,他是一个公共网络服务


分布式锁的应用场景

如下图。买票的逻辑:{if(查看票数是否为0)票数--;},这里分为两步,所以会有并发安全问题,即:多个买票服务器并发访问数据库可能会出现“超卖”。
有的同学可能会想:这好办,Mysql一类的数据库有事务,可以把多个操作变成原子的,可惜的是,这并不是一个好主意,在Mysql中就算是“串行化”也不是完全串行的,两个事务同时读一条数据总是被允许的,假设只剩一张票,两个事务同时读它发现有票,然后都进入下一步:修改票数,就算他们的修改是串行的,也会都修改一次,还是“超卖”。

这种情况下我们只能使用分布式锁来解决,充当锁的公共服务可以是redis也可以是我们自己写的服务,总之,它要可以充当锁,所以架构就变成下面这样(以redis为例):

我们完全可以通过命令setnx和del进行加锁和解锁。由于redis单线程,执行这些命令都是原子的。

  • 加锁的时候就是setnx:如果执行成功就表示加锁成功;如果已经存在就执行失败,表示加锁失败。
  • 解锁的时候就是del删除相应的键值对

考虑这样一种情况,某个买票服务加锁成功后就宕机了,这样锁就会一直存在,其他买票服务也无法进行加锁

  • 为了避免这种情况,在加锁成功的同时,也要给这个键值对设置一个过期时间,让它超时自动删除。set ex nx 命令就支持同时设置键值对和过期时间。但要注意,不可以把他们分成两个命令,redis的事务是不安全的,假设键值对设置失败单过期时间设置成功也是不会回滚的,所以要避免这样做。

上述方案仍然存在一个重要问题:当设置了 key 的过期时间(比如 10s)后,仍有可能在任务尚未执行完时,key 就已过期,导致锁提前失效

那么,把过期时间设得足够长(比如 30s)是否能解决呢?显然,设置多长合适是个无止境的问题,即便设再长,也无法完全保证不会提前失效。而且,设得太长的话,万一对应服务器宕机,其他服务器也无法及时获取到锁。因此,相较于固定一个较长的过期时间,不如动态调整时间更为合适。
所谓Watch Dog(看门狗),本质上是加锁服务器上的一个独立线程,通过该线程对锁过期时间进行“续约”。注意,这个线程是业务服务器上的,而非 Redis 服务器。
举个具体例子:
初始设置过期时间为 10s,同时设定看门狗线程每隔 3s 检测一次。当 3s 到达时,看门狗会判断当前任务是否完成:

  • 如果任务已完成,则直接通过 Lua 脚本释放锁(删除 key);
  • 如果任务未完成,则将过期时间重新设置为 10s(即“续约”)。

这样一来,既不必担心锁提前失效;另一方面,如果该服务器宕机,看门狗线程也随之消失,无人续约,key 自然能快速过期,从而让其他服务器得以获取锁。

再考虑这样一种情况,一个买票服务加锁,另一个买票服务直接给把锁del了。比如,服务器1 写入一个"001": 1这样的键值对,服务器2 完全可以把"001"给删除掉。当然,服务器2 不会进行这样的“恶意删除”操作,不过不能保证因为一些 bug 导致服务器2 把锁误删除

  • 为了解决上述问题,我们可以引入一个校验 id。比如,可以把设置的键值对的值,不再是简单地设为1,而是设成服务器的编号,形如"001": "服务器1"。这样就可以在删除 key(解锁)的时候,先校验当前删除 key 的服务器是否是当初加锁的服务器,如果是,才能真正删除;不是,则不能删除。逻辑用伪代码描述如下:
    String key = [要加锁的资源 id]; String serverId = [服务器的编号]; // 加锁,设置过期时间为 10s redis.set(key, serverId, "NX", "EX", "10s"); // 执行各种业务逻辑,比如修改数据库数据 doSomeThing(); // 解锁,删除 key。但是删除前要检验下 serverId 是否匹配 if (redis.get(key) == serverId) { redis.del(key); }

    但是很明显,解锁逻辑是两步操作getdel,这样做并非原子的。但是我们可以使用redis支持的lua脚本执行这段逻辑,redis会先把这段程序执行完才去执行其他命令,保证了原子性。

在考虑最后一种情况,如果redis服务也就是锁服务本身挂掉了怎么办

首先我们同学们可能会想到用主从架构,但是主从架构是有延迟的,没同步之前主节点就挂了这也是可能得。redis作者给出了一种redlok算法:直接搞多台锁服务,应用服务需要对它们轮流加锁和解锁,超过总的锁服务的数目的一半才算加锁/解锁成功。

http://www.gsyq.cn/news/1644383.html

相关文章:

  • 计组面试--h自用
  • Lua--协同线程与文件IO
  • 小红书博主都在偷偷用的AI工具,不用懂代码就能自动运营
  • 智能办公本X2:端侧AI驱动的手写语音协同工作流
  • Lua--基础入门
  • 2000+机柜怎么管?数据中心U位资产管理方案拆解
  • 完整RAG工作流达成!手把手教你使用NAS部署企业生产级AI知识库
  • 库存并发安全控制的架构设计
  • 谷歌两款AI学习工具大揭秘:NotebookLM与Learn About谁更胜一筹?
  • 别再硬写提示词了!LangChain PromptTemplate从入门到实战
  • GEO代理接单后总部负责落地吗
  • PowerShell 路径规则详解:从基础到高级
  • 2026杭州初中毕业女生暑假学什么好?选对方向比努力更重要
  • 剪映专业版教程:制作西施跳广场舞效果
  • MLflow在LLM评估中的工程实践:实现可追溯、可比较、可归因的模型管理
  • 06-高级模式与实战项目——01. Render Props - 共享渲染逻辑
  • TFT-LCD 驱动架构对比:4 种 Cs 存储电容布局的优缺点与选型指南
  • 私密科普:女性经后淋漓不尽,别当成普通经期残留
  • 机房故障换机后应急验证:24 小时 SpeedCE 点检 SOP
  • AI编程助手实战指南:从原理到应用,GitHub Copilot与Cursor深度测评
  • 白话MVP
  • Cline 配置 Claude Sonnet 5 实战指南:思考深度调优与切换 Fable 5 的时机
  • Redis--Redis分布式系统的原理与实操
  • 图最短路评测:Dijkstra 对了,也可能用错场景
  • 74LS73 异步计数器设计实战:2片芯片实现4位二进制与8421BCD电路对比
  • [特殊字符] Git 协作指南
  • Claude Code的完美国产替代小米 MiMo Code安装指南
  • CameraGraph™全域相机拓扑推理引擎 视频孪生跨镜目标连续追踪核心支撑 空间相机关系张量建模:根治跨镜头目标ID跳变、身份混淆底层算法拆解
  • 2025反爬系统深度解析:从Canvas指纹到AI行为画像的攻防实战
  • ML预测半导体良品率——样本缺失值模式分析(Python+Pandas+Matplotlib)