Redis篇之分布式锁

一、为什么要使用分布式锁

1.抢劵场景

(1)代码及流程图

(2)抢劵执行的正常流程

        就是正好线程1执行完整个操作,线程2再执行。

                              

(3)抢劵执行的非正常流程

        因为线程是交替进行的,所以有可能线程1查询后,线程2再查询。假如现在只有一张票,所以线程1会买到这张票,并扣除库存,现在也就是没有票了。但是线程2查询的时候,库存是有一张票的,所以也会进行库存扣除,变成-1张了。这样就出现了超卖的问题了。

                               

(4)那怎么解决(3)的问题呢

        1. 首先想到的就是普通的加锁,如下图所示:

       

        2. 这样会不会出现问题呢?当然有可能,在实际项目中,服务器大部分都是集群部署的,因为普通加锁只能解决单个服务器的互斥,不能解决多个服务器线程的互斥,所以还是有问题。

        

        3. 那应该怎么解决呢?就是我们提及到的使用分布式锁,这样就可以限制其他服务器也获取锁的问题,只有当一台服务器解锁,其他服务器才能拿到锁。

二、redis分布式锁

1.核心命令

        Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则 SET)的简写。

2.如何使用

        1. 命令设置,为什么获取锁要把超时时间一起写上去呢?还有为什么一定要设置过期时间呢?第一,不分开写就是要保证数据的原子性第二,不设置过期时间可能会导致死锁。可以看执行流程图,如果服务器宕机,会导致锁不能释放,造成死锁,所以要加过期时间兜底。

        2. 执行流程:

3.如何控制锁的有效时长呢

        1.根据业务执行时间预估。这种方法往往不能保证原子性,一般不使用。

        2.给锁续期。就是根据执行业务的时长进行锁时长的增加。那自己还需要开一个线程专门来监控,实现起来也比较麻烦,所以会使用redission实现的分布式锁

三、redission实现的分布式锁

1.redission实现分布式锁的流程

(1)流程图

(2)解释流程图的步骤

        首先,线程1获取锁成功后,采用 watch-dog来检查和更新锁状态,每隔(过期时间/3)的时间续期一次,然后操作redis,手动释放锁后,会通知watch-dog不用监听锁了。

        如果线程2也需要加锁,但是没有获取到锁,则会while循环去尝试获取锁,循环的次数也不是无限的,而是到达一定阈值就停止。也不用担心因为循环而导致获取锁失败,因为在高并发的情况下,业务时长不会太长,所以会在这种情况下可以增加分布式锁的使用性能。这也是分布式锁的一个特性:等待机制

(3)具体代码

public void redisLock() throws InterruptedException{//获取重入锁,执行锁的名字(自定义)RLock lock = redissionClient.getLock("nameLock");try {//尝试获取锁,参数分别是:锁的最大等待时间,锁自动释放时间,时间单位boolean isLock = lock.tryLock(10,30,TimeUnit.SECONDS);//判断是否获取锁成功if(isLock){//业务代码}}finally {//释放锁lock.unlock();}}

关键点:加锁、设置过期时间等操作都是基于lua脚本完成的,目的是保证数据的原子性

2.redission分布式锁的可重入

(1)例子代码

public void add1() {RLock lock = redissonClient.getLock("nameLock");boolean isLock = lock.tryLock();//执行业务add2();// 释放锁lock.unlock();}public void add2() {RLock lock = redissonClient.getLock("nameLock");boolean isLock = lock.tryLock();//执行业务// 释放锁lock.unlock();}

(2)如何实现呢

        利用hash结构记录线程id重入次数。其中key就是锁的名字,field就是线程的唯一标识,value就是重入的次数。根据上面代码,add1方法先获取锁后,value的值就是1。然后调用add2方法,add2方法也获取该锁,所以value的值就变成2。然后add2解锁,value就减一变成1。回到add1方法中,释放锁,value再减一,最后value就变成了0。最后当value为0时,就可以删除锁。

        关键点:必须是在同一个线程才能重入。

3.redission分布式锁的主从一致性

(1)非正常情况

        1. 当主节点宕机。

        2. 其中一个从节点会代替主节点。

        3. 如果这时线程2也来获取锁,因为线程1获取锁后,主节点宕机了,所以线程2会访问新的主节点,获取锁成功。这样会出现,两个线程获取同一把锁,就有问题了。

(2)如何解决(1)的问题

        RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n / 2 + 1),避免在一个redis实例上加锁。由于缺点太多了。实际中并不会大量使用redission的红锁。

        redis:AP思想。

        zookeeper:CP思想。

        所以,实际中就是 使用zookeeper来解决主从一致性。

四、面试的时候怎么说

面试官:Redis分布式锁如何实现 ?

候选人:嗯,在redis中提供了一个命令setnx(SET if not exists)

由于redis的单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候是其他客户端是不能设置这个key的

面试官:好的,那你如何控制Redis实现分布式锁有效时长呢?

候选人:嗯,的确,redis的setnx指令不好控制这个问题,我们当时采用的redis的一个框架redisson实现的。

在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了

还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自旋不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。

面试官:好的,redisson实现的分布式锁是可重入的吗?

候选人:嗯,是可以重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数

面试官:redisson实现的分布式锁能解决主从一致性的问题吗

候选人:这个是不能的,比如,当线程1加锁成功后,master节点数据会异步复制到slave节点,此时当前持有Redis锁的master节点宕机,slave节点被提升为新的master节点,假如现在来了一个线程2,再次加锁,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。

我们可以利用redisson提供的红锁来解决这个问题,它的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis节点上都成功创建锁,红锁中要求是redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题了。

但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变的很低了,并且运维维护成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁。

面试官:好的,如果业务非要保证数据的强一致性,这个该怎么解决呢?

候选人:嗯~,redis本身就是支持高可用的,做到强一致性,就非常影响性能,所以,如果有强一致性要求高的业务,建议使用zookeeper实现的分布式锁,它是可以保证强一致性的。

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

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

相关文章

Vagrant 虚拟机工具基本操作指南

Vagrant 虚拟机工具基本操作指南 ​#虚拟机 #​ ​#vargant#​ ​#ubuntu#​ ‍ 虚拟机virtualbox ,VMWare及WSL等大家都很了解了,那Vagrant是什么东西? 它是一组命令行工具,可以象Docker管理容器一样管理虚拟机,这样快速创…

Java代码实现基数排序算法(附带源码)

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。 1. 基数排序…

缓存组件Caffeine的使用

caffeine是一个高性能的缓存组件&#xff0c;在需要缓存数据&#xff0c;但数据量不算太大&#xff0c;不想引入redis的时候&#xff0c;caffeine就是一个不错的选择。可以把caffeine理解为一个简单的redis。 1、导入依赖 <!-- https://mvnrepository.com/artifact/com.git…

Sublime Text 3配置 Node.js 开发环境

《开发工具系列》 Sublime Text 3配置 Node.js 开发环境 一、引言二、主要内容2.1 初识 Sublime Text 32.2 初识 Node.js2.3 接入 Node.js2.3.1 下载并安装 Node.js2.3.2 环境变量配置 2.4 配置 Node.js 开发环境2.5 编写 Node.js 代码2.6 运行 Node.js 代码 三、总结 一、引言…

网站服务器中毒或是被入侵该怎么办?

随着互联网的普及和发展&#xff0c;网站服务器已经成为企业和个人不可或缺的资源。然而&#xff0c;网络安全问题也日益突出&#xff0c;其中服务器中毒或被入侵是常见的问题之一。一旦服务器中毒或被入侵&#xff0c;不仅会导致数据泄露、网站瘫痪等严重后果&#xff0c;还可…

Ubuntu22.04切换系统cuda版本

由于最近项目要求的cuda版本有差异&#xff0c;而在Ubuntu中可以通过切换cuda来满足需求&#xff0c;现记录如下。 1、按照 Ubuntu22.04与深度学习配置 中的cuda安装章节&#xff0c;将需要的cuda版本下载到本地并进行安装。 2、cuda安装完成后修改bashrc文件内容 sudo gedit …

2024 Google Chrome 浏览器回退安装旧版本

2024 Google Chrome 浏览器回退安装旧版本 查看当前谷歌版本备份浏览器数据卸载浏览器双击重新安装旧版本浏览器 查看当前谷歌版本 详细参考&#xff1a;参考 笔记&#xff1a;最近谷歌浏览器更新后&#xff0c;用着总感觉别扭&#xff1a;不习惯 备份浏览器数据 &#xff…

MySQL ——group by子句使用with rollup

group by 子句使用with rollup关键字之后&#xff0c;具有分组加和的功能。即&#xff1a;在所有的分组记录之后&#xff0c;自动新增一条记录&#xff0c;从全局计算所有记录的数据。 0 问题描述 求出每年的学生平均成绩&#xff0c;及历史至今的平均成绩&#xff0c;结果保留…

高可用 k8s 1.29 一键安装脚本, 丝滑至极

博客原文 文章目录 集群配置配置清单集群规划集群网络规划 环境初始化主机配置 配置高可用ApiServer安装 nginx安装 Keepalived 安装脚本需要魔法的脚本不需要魔法的脚本配置自动补全加入其余节点 验证集群 集群配置 配置清单 OS&#xff1a; ubuntu 20.04kubernetes&#xf…

C++三剑客之std::any(一) : 使用

相关系列文章 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)&#xff1a;深入剖析​​​​​​​ 目录 1.概述 2.构建方式 2.1.构造函数 2.2.std::make_any 2.3.operator分配新值 3.访问值…

猫头虎分享已解决Bug || Go Error: imported and not used: ‘fmt‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

EMC学习笔记(二十一)降低EMI的PCB设计指南(一)

降低EMI的PCB设计指南&#xff08;一&#xff09; 1.概述2.射频3.连接器与过孔元件4.静态引脚和动态引脚和输入5.基本回路6.差模与共模 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 1.概述 印刷电路板(PCB)的一般布局准则&#xff0c;基本上都有相对的文件进…

Phobos捆绑某数控软件AdobeIPCBroker组件定向勒索

前言 Phobos勒索病毒最早于2019年被首次发现并开始流行起来&#xff0c;该勒索病毒的勒索提示信息特征与CrySiS(Dharma)勒索病毒非常相似&#xff0c;但是两款勒索病毒的代码特征却是完全不一样&#xff0c;近日笔者在逛某开源恶意软件沙箱的时候发现了一款Phobos勒索病毒捆绑…

ad18学习笔记十八:如何放置丝印层敷铜?

我画板的时候&#xff0c;需要把板卡顶面丝印层的一个矩形区域&#xff0c;画成白色&#xff0c;但是这个区域内有好几个焊盘&#xff0c;丝印涂色的地方需要避开这几个焊盘&#xff0c;我觉得不能简单的在丝印层画一个矩形完事&#xff0c;最好让丝印层的这个区域&#xff0c;…

Docker容器化K8s集群部署教程(一键部署sheel脚本)

本文通过脚本&#xff0c;可以快速地部署和配置Kubernetes环境&#xff0c;省去了各插件手动部署、配置的繁琐过程。 先看最终结果&#xff1a; [rootlocalhost home]# kubectl get node NAME STATUS ROLES AGE VERSION k8smaster Ready control-p…

计网——运输层、端口号

目录 运输层 1 进程之间的通信 运输层的作用 屏蔽作用 可靠信道与不可靠信道 2 运输层的两个主要协议 3 运输层的端口 端口号 (protocol port number) 软件端口 硬件端口 TCP/IP 运输层端口的标志 两大类、三种类型的端口 常用的熟知端口 运输层 1 进程之间的通信 …

【Spring源码解读!底层原理进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

「深度学习」循环神经网络RNN

一、序列模型的例子 二、数学符号定义 X^{(i)<t>}&#xff1a;训练样本 i 的输入序列的第 t 个元素。 T_{X}^{i}&#xff1a;训练样本 i 的输入序列的长度。 Y^{(i)<t>}&#xff1a;训练样本 i 的输出序列的第 t 个元素。 T_{Y}^{i}&#xff1a;训练样本 i 的输…

[大厂实践] Netflix容器平台内核panic可观察性实践

在某些情况下&#xff0c;K8S节点和Pod会因为出错自动消失&#xff0c;很难追溯原因&#xff0c;其中一种情况就是发生了内核panic。本文介绍了Netflix容器平台针对内核panic所做的可观测性增强&#xff0c;使得发生内核panic的时候&#xff0c;能够导出信息&#xff0c;帮助排…

Linux(二)

远程登录 Linux大多应用于服务器&#xff0c;而服务器不可能像PC一样放在办公室&#xff0c;它们是放在IDC机房的&#xff0c;所以我平时登录linux系统都是通过远程登录的。 Linux系统中是通过ssh服务实现的远程登录功能。默认ssh服务开启了22端口&#xff0c;而且当我们安装…