Redis 分布式锁

Redis分布式锁 - 胤凯 (oyto.github.io)

分布式锁

1、什么是分布式锁?

在分布式场景下的锁,比如在多台不同机器上的进程,去竞争同一项资源,就是分布式锁。

2、分布式锁有哪些特性?
  • 互斥性:只能让一个竞争者持有锁

  • 安全性:避免锁因为异常永远不被释放,当一个竞争者在持有锁期间,由于意外崩溃而导致未能主动解锁,其持有的锁也能够被兜底释放,并保证后续其它竞争者也能加锁

  • 对称性:同一个锁,加锁和解锁必须是一个竞争者,不能把其他竞争者持有的锁给释放了

  • 可靠性:需要有一定程度的异常处理能力、容灾能力。

3、分布式锁的实现方式
最简化版本

直接用 Redis 的 setnx 命令,语法:setnx key value,如果 key 不存在,则会将 key 设置为 value,并返回 1;如果 key 存在,不会有任务影响,返回 0。

通过 setnx 加锁,其他服务无法加锁,进而阻塞;用完之后,通过 delete 解锁,其他服务再去竞争锁。

支持过期时间

最简化版本有一个问题:如果获取锁的服务挂掉了,那么锁就一直得不到释放,就会导致其他服务无法获取到锁,影响到其他服务,所以这里需要一个过期时间来进行兜底。

Redis 中有 expire 命令,用来设置一个 key 的过期时间。但是 setnx 和 expire 不具备原子性,如果 setnx 获取锁之后,服务挂掉,还没来得及设置过期时间,照样石沉大海。

于是使用 set 和 expire 的原子操作:set key value nx ex seconds nx 标识 setnx 特性,ex 标识过期时间,最后一个参数就是过期时间的值。

加上过期时间,基本上这个锁就能用了。但存在一个问题:会存在服务 A 释放掉 服务 B 的锁的可能。

加上 owner

在特殊的场景:服务 A 获取到了锁,由于业务流程比较长或者网络延迟、GC卡顿等原因,导致锁过期,而业务还会继续进行,这时候,业务 B 已经拿到了锁,准备去执行。这个时候服务 A 恢复过来并做完了任务,就会释放锁,而 B 还在继续,就会导致服务 A 释放掉了 服务 B 的锁。

在真实的分布式场景中,可能存在几十个竞争者,上述情况发生的概率就会很高,导致同一份资源频繁被不同竞争者同时访问,分布式锁也就失去了意义。

发生这个问题的关键在于:竞争者可以释放掉其他竞争者的锁。所以我们可以给出进一步的解决方案:分布式锁需要满足谁申请谁释放原则,不能释放别人的锁,也就是说,分布式锁,是要有归属的。

引入 lua

加入 owner 后的版本,也还有一点点小问题。完整的流程是:竞争者获取到锁执行任务,执行完毕后检查锁是不是自己的,最后释放。

这些操作都不是原子化的,可能锁获取的时候还是自己的,删除的时候已经是别人的了。

这里就需要引入 Lua。

Redis + Lua,可以说是专门为解决原子问题而生的。

到了这里,分布式锁的前三个特性已经满足:对称性、安全性、互斥性。可以是一个可以用的分布式锁了,能满足大多数场景。

4、可靠性如何保证

还剩下可靠性没有解决。

针对一些异常情景,包括 Redis 挂掉、业务执行时间过长、网络波动等情况。

容灾考虑

前面我们谈及的内容,基本是基于单机考虑的,如果Redis挂掉了,那锁就不能获取了。这个问题该如何解决呢? ​ 一般来说,有两种方法:主从容灾和多级部署。

主从容灾

最简单的方式,就是为 Redis 配置从节点,当主节点挂掉了,从节点顶包。

主从切换的话,需要人工参与,会提高人力成本。不过 Redis 已经有成熟的解决方案,也就是哨兵模式,可以灵活自动切换,不再需要人工介入。

虽然一定程度解决了单点的容灾问题,但并不是尽善尽美的由于同步有时延,slave通过增加从节点的方式,可能会损失掉部分数据,分布式锁可能失效,这就会发生短暂的多机获取到执行权限

有没有更可靠的办法呢?

多机部署

如果对一致性高一些,可以尝试多机部署。比如 Redis 的 RedLock,大概思路就是多个机器,通常是奇数,达到一半以上同意才能算加锁成功,这样可靠性会向 ETCD 靠近。

ETCD:etcd:在前面的回答中已经介绍过,etcd是一个分布式键值存储系统,用于配置管理、服务发现和分布式协调。它是一个独立的开源项目,由CNCF维护,具有强一致性和高可用性,用于构建分布式系统的基础设施。

现在假设有5个Redis主节点,基本保证它们不会同时宕掉,获取锁和释放锁的过程中客户端会执行以下操作:

  1. 向5个Redis申请加锁;

  2. 只要超过一半,也就是3个Redis返回成功,那么就是获取到了锁。如果超过一半失败,需要向每个Redis发送解锁命令;

  3. 由于向5个Redis发送请求,会有一定时耗,所以锁剩余持有时间,需要减去请求时间。这个可以作为判断依据,如果剩余时间已经为0,那么也是获取锁失败;

  4. 使用完成之后,向5个Redis发送解锁请求。

这种模式的好处在于,如果挂了2台Redis,整个集群还是可用的,给了运维更多时间来修复。

另外,多说一句,单点Redis的所有手段,这种多机模式都可以使用,比如为每个节点配置哨兵模式,由于加锁是一半以上同意就成功,那么如果单个节点进行了主从切换,单个节点数据的丢失,就不会让锁失效了。这样增强了可靠性。

没有完全可靠的分布式锁

由于分布式系统中的三大困境,简称 NPC。

  1. N:Network Delay (网络延迟)网络延迟导致锁过期。

  2. P:Process Pause(进程暂停)比如发生 GC,导致锁超时。

  3. C:Clock Drift(时钟漂移)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/96460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 将字符串转为Base64格式与将Base64内容解析出来

首先要引入依赖包 import java.nio.charset.StandardCharsets; import java.util.Base64;然后对应一下两个代码 将字符串转为Base64 Base64.getEncoder().encodeToString(需要转换的字符串.getBytes(StandardCharsets.UTF_8));将 Base64 字符串解析成原来的内容 byte[] deco…

每日一题 901. 股票价格跨度(中等,单调栈)

理解题目,对于第 i 天,要求的是前 i - 1 天所满足条件的跨度 思路: 暴力搜索的方式是,对于每一个第 i 天都遍历搜索 i - 1, i - 2,…,直到第 j 天大于当前价格优化,考虑哪里进行了…

SQL的CASE WHEN函数、CAST函数、CONVERT() 函数、COALESCE()函数、DATEDIFF()函数

一、CASE WHEN简单使用 SELECT CASE WHEN age > 18 AND age < 25 THEN 18-25WHEN age > 25 AND age < 35 THEN 25-35WHEN age > 35 AND age < 45 THEN 36-45ELSE 45END AS age_groupFROM peopleGROUP BY age_group;二、CASE WHEN语句与聚合函数一起使用 SE…

论文总结:3D Talking Face With Personalized Pose Dynamics

论文解决的问题:大多数现有的3D人脸生成方法只能生成静态头部姿势的3D面部,只有少数几篇文章关注头部姿势的生成,但这些文章也忽略了个体属性。 解决方法:框架由两个独立模块组成:PoseGAN和PGFace。给定输入音频,PoseGAN首先为3D头部生成一个头部姿势序列,然后PGFace利用…

blender光照系统设置

0&#xff09;Viewport Shading设置里面的Lighting下面的参数&#xff1a; Scene Lights,Scene World - Scene Lights是指在渲染模式下是否使用场景中的灯光对象来照亮物体。 - Scene World是指在渲染模式下是否使用场景中的世界设置来作为背景和环境光。如果关闭该选项&#…

数据库基础知识

数据库 什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员? 数据库 : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。数据库管理系统 : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大…

Redis Cluster Gossip Protocol: FAIL, UPDATE

返回目录 FAIL的发送 过程 构建消息头把处于FAIL状态的node的ID填入消息的数据部分广播消息 遍历cluster节点字典:跳过还没有创建连接的node跳过myself和处于handshake的node给node发送FAIL消息FAIL的接收处理 过程 第1 ~ 3步是涵盖所有类型的消息&#xff0c;详细请参考…

RPA自动化全平台文章同步助手

在当今文案自媒体时代&#xff0c;我们通常在各大平台都拥有账号&#xff0c;需要同步发布文章。然而&#xff0c;这个过程常常让人感到非常繁琐&#xff0c;因为我们需要将文章复制粘贴到不同平台上。但是&#xff0c;现在我们可以借助RPA&#xff08;Robotic Process Automat…

TL-ER3220G端口映射设置

1、打开IE浏览器或其它浏览器&#xff0c;在地址栏输入192.168.1.1登录路由器的Web管理界面&#xff1b; 2、打开后弹出密码输入框&#xff0c;输入路由器的用户名和密码&#xff0c;出厂默认值为admin/admin&#xff0c;成功登录后将看到路由器的系统状态信息&#xff1b; 3、…

java技术文档--多线程(1)--核心学习大纲--首页

阿丹&#xff1a; 对于java来说最核心也是一个java程序员最大竞争力的就是多线程的开发。希望我的这一套多线程学习的体系可以帮助大家整理以及清楚明白的使用多线程&#xff0c;并了解多线程的底层。其实不少同学在提到多线程的时候一下就头大了。没关系阿丹将多线程这里用深…

Setup Factory 使用及删除文件夹

前一段时间&#xff0c;使用Setup Factory 进行打包&#xff0c;在使用过程中&#xff0c;遇到了很多问题&#xff0c;在网上找了很多资料&#xff0c;安装包制作工具 SetupFactory 详解_setup factory-CSDN博客&#xff0c;这篇文章介绍的很详细&#xff0c;可以用做参考。 关…

Python中如何获取各种目录路径

最近总是遇到各种路径问题&#xff0c;学习总结一下 文章目录 1. 获取各种目录的方法&#xff1a;2. Python测试脚本&#xff1a; 1. 获取各种目录的方法&#xff1a; 当前工作目录&#xff1a;这是你从哪里运行了你的Python脚本。 current_directory os.getcwd()脚本所在目录…

学习搜狗的workflow,MacBook上如何编译

官网说可以在MacBook上也可以运行&#xff0c;但是编译的时候却有找不到openssl的错误&#xff1a; 看其他博客也有类似的错误&#xff0c;按照类似的思路去解决 问题原因和解决办法 cmake编译的时候&#xff0c;没有找到openssl的头文件&#xff0c;需要设置cmake编译环境下…

解决docker开启MySQL的binlog无法成功。docker内部报错:mysql: [ERROR] unknown variable

1. 报错信息 2. 操作流程 整个流程是这样的&#xff1a; 我愉快的输入docker ps&#xff0c;查看MySQL的docker 容器id 执行指令docker exec -it 8a \bin\bash进入容器内部执行vim /etc/my.cnf&#xff0c;打开配置文件按照网上说的&#xff0c;添加如下配置信息退出docker容…

解决若依框架多次list查询时,分页失效问题

一、问题背景 当若依框架遇到两次及以上的list查询时,会引发分页查询失效问题,如下图: 二、分析原因 分页查询原理 Mybatis的分页原理&#xff0c;大致就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL&#xff0c;实现分页…

2023(2024届)计算机保研经验分享,圆梦山东大学

前言&#xff1a; Hello大家好&#xff0c;我是Dream&#xff0c;好久不见啦&#xff01;在这不见的半年多时间里我一直在全身心的投入保研之中&#xff0c;在写下这份面经时&#xff0c;真的是感慨颇多&#xff0c;思绪万千。站在这个时间点上&#xff0c;回首过去的几个月&am…

Go:实现SMTP邮件发送订阅功能(包含163邮箱、163企业邮箱、谷歌gmail邮箱)

需求很简单&#xff0c;就是用户输入自己的邮箱后&#xff0c;使用官方邮箱给用户发送替邮件模版 目录 前置邮件模版邮箱开启SMTP服务163邮箱163企业邮箱谷歌gmail邮箱腾讯企业邮箱-失败其他邮箱-未操作 邮件发送核心代码config.yaml配置读取邮件相关配置发送邮件 附录 前置 邮…

C++中迭代器的使用

C中的迭代器是一种用于遍历容器&#xff08;如数组、向量、列表、映射等&#xff09;中元素的工具。迭代器提供了一种通用的方式来访问容器中的数据&#xff0c;而不依赖于容器的具体实现。 以下是C中迭代器的基本使用方法和示例&#xff1a; 1.迭代器类型&#xff1a; C标准…

使用关键字interface来声明使用接口-PHP8知识详解

继承特性简化了对象、类的创建&#xff0c;增加了代码的可重用性。但是php8只支持单继承&#xff0c;如果想实现多继承&#xff0c;就需要使用接口。PHP8可以实现多个接口。 接口类通过关键字interface来声明&#xff0c;接口中不能声明变量&#xff0c;只能使用关键字const声明…

tcpdump(一)基础理论知识

一 抓包分析技术初探 说明&#xff1a; 本篇章跟tcp/ip的知识没有关系,只是讲解tcpdump工具背景补充&#xff1a; 抓包是做报文分析的第一步敬畏心&#xff1a; 隔行如隔山,不要想当然 ① 背景 ② 抓包技术名词 1、捋顺这些技术的来龙去脉甚至八卦;2、这样我们在后续课程…