与分布式锁相对应的是「单机锁」,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来「互斥」,以保证共享变量的正确性,其使用范围是在「同一个进程」中。

一、为什么需要分布式锁?

在开始讲分布式锁之前,有必要简单介绍一下,为什么需要分布式锁?

与分布式锁相对应的是「单机锁」,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来「互斥」,以保证共享变量的正确性,其使用范围是在「同一个进程」中。

如果换做是多个进程,需要同时操作一个共享资源,如何互斥呢?

例如,现在的业务应用通常都是微服务架构,这也意味着一个应用会部署多个进程,那这多个进程如果需要修改 MySQL 中的同一行记录时,为了避免操作乱序导致数据错误,此时,我们就需要引入「分布式锁」来解决这个问题了。

想要实现分布式锁,必须借助一个外部系统,所有进程都去这个系统上申请「加锁」。

而这个外部系统,必须要实现「互斥」的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败(或等待)。

这个外部系统,可以是 MySQL,也可以是 Redis 或 Zookeeper。但为了追求更好的性能,我们通常会选择使用 Redis 或 Zookeeper 来做。

下面我就以 Redis 为主线,由浅入深,带你深度剖析一下,分布式锁的各种「安全性」问题,帮你彻底理解分布式锁。

二、分布式锁怎么实现?

我们从最简单的开始讲起。

想要实现分布式锁,必须要求 Redis 有「互斥」的能力,我们可以使用 SETNX 命令,这个命令表示SET if Not eXists,即如果 key 不存在,才会设置它的值,否则什么也不做。

两个客户端进程可以执行这个命令,达到互斥,就可以实现一个分布式锁。

客户端 1 申请加锁,加锁成功:

127.0.0.1:6379> SETNX lock 1
(integer) 1     // 客户端1,加锁成功

客户端 2 申请加锁,因为后到达,加锁失败:

127.0.0.1:6379> SETNX lock 1
(integer) 0     // 客户端2,加锁失败

此时,加锁成功的客户端,就可以去操作「共享资源」,例如,修改 MySQL 的某一行数据,或者调用一个 API 请求。

操作完成后,还要及时释放锁,给后来者让出操作共享资源的机会。如何释放锁呢?

也很简单,直接使用 DEL 命令删除这个 key 即可:

127.0.0.1:6379> DEL lock // 释放锁
(integer) 1

这个逻辑非常简单,整体的路程就是这样:

但是,它存在一个很大的问题,当客户端 1 拿到锁后,如果发生下面的场景,就会造成「死锁」:

程序处理业务逻辑异常,没及时释放锁

进程挂了,没机会释放锁

这时,这个客户端就会一直占用这个锁,而其它客户端就「永远」拿不到这把锁了。

怎么解决这个问题呢?

三、如何避免死锁?

我们很容易想到的方案是,在申请锁时,给这把锁设置一个「租期」。

在 Redis 中实现时,就是给这个 key 设置一个「过期时间」。这里我们假设,操作共享资源的时间不会超过 10s,那么在加锁时,给这个 key 设置 10s 过期即可:

127.0.0.1:6379> SETNX lock 1    // 加锁
(integer) 1
127.0.0.1:6379> EXPIRE lock 10  // 10s后自动过期
(integer) 1

这样一来,无论客户端是否异常,这个锁都可以在 10s 后被「自动释放」,其它客户端依旧可以拿到锁。

疑问脸,但这样真的没问题吗?

还是有问题。

现在的操作,加锁、设置过期是 2 条命令,有没有可能只执行了第一条,第二条却「来不及」执行的情况发生呢?例如:

  • SETNX 执行成功,执行 EXPIRE 时由于网络问题,执行失败
  • SETNX 执行成功,Redis 异常宕机,EXPIRE 没有机会执行
  • SETNX 执行成功,客户端异常崩溃,EXPIRE 也没有机会执行

总之,这两条命令不能保证是原子操作(一起成功),就有潜在的风险导致过期时间设置失败,依旧发生「死锁」问题。

那怎么办呢?

在 Redis 2.6.12 版本之前,我们需要想尽办法,保证 SETNX 和 EXPIRE 原子性执行,还要考虑各种异常情况如何处理。

但在 Redis 2.6.12 之后,Redis 扩展了 SET 命令的参数,用这一条命令就可以了:

// 一条命令保证原子性执行
127.0.0.1:6379> SET lock 1 EX 10 NX
OK

这样就解决了死锁问题,也比较简单。

基于Redis实现一个分布式锁相关推荐

  1. redistemplate分布式锁实现_基于 Redis SETNX 实现分布式锁

    环境与配置 Redis 任意版本即可 SpringBoot 任意版本即可,但是需要依赖 spring-boot-starter-data-redis <dependency><gro ...

  2. 基于 Redis 实现的分布式锁

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:我的大学到研究生自学 Java 之路,过程艰辛,不放弃,保持热情,最终发现我是这样拿到大厂 offer 的! 作 ...

  3. 【Redis笔记】一起学习Redis | 如何利用Redis实现一个分布式锁?

    一起学习Redis | 如何利用Redis实现一个分布式锁? 前提知识 什么是分布式锁? 为什么需要分布式锁? 分布式锁的5要素和三种实现方式 实现分布式锁 思考思考 基础方案 改进方案 保证setn ...

  4. 小王,在 Java 中如何利用 redis 实现一个分布式锁服务呢???

    作者:杨高超 juejin.im/post/5a4984af6fb9a0450b66bc57 在现代的编程语言中,接触过多线程编程的程序员多多少少对锁有一定的了解.简单的说,多线程中的锁就是在多线程环 ...

  5. 字节面试:如何用Redis实现一个分布式锁?

    在开始提到Redis分布式锁之前,我想跟大家聊点Redis的基础知识. 说一下Redis的两个命令: SETNX key value setnx 是SET if Not eXists(如果不存在,则 ...

  6. 关于分布式锁的续命问题——基于Redis实现的分布式锁

    目录 一.背景 二.自定义实现 三.Redisson框架实现 一.背景 关于分布式锁就不多说了,现在出现了一种场景,如果在分布式锁中,业务代码没有执行完,然后锁的键值过期了,那么其他的JVM就可能会获 ...

  7. Redis 集群分布式锁与 API 网关分布式限流

    https://www.infoq.cn/article/FoQGIk*BzdQWJJ0tKqrJ Redis 集群的历史 Redis 在 3.0 前一般有两种集群方案,一是 proxy(Twempr ...

  8. Redlock——Redis集群分布式锁

    欢迎关注方志朋的博客,回复"666"获面试宝典 前言 分布式锁是一种非常有用的技术手段.实现高效的分布式锁有三个属性需要考虑: 安全属性:互斥,不管什么时候,只有一个客户端持有锁 ...

  9. redis系列:分布式锁

    1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境, ...

最新文章

  1. 新兴内存技术准备突围
  2. C++ 智能指针(unique_ptr / shared_ptr)代码实现
  3. mysql 截断表_入门MySQL——基础语句篇
  4. 完美解答35K月薪的MySQL面试题(四)MySQL是如何加行锁的?
  5. mac中NSScrollView自定义滑动条NSScroller
  6. 方法~作用于对象~失败_消息三:ActiveMQ Topic 消息失败重发
  7. YFI 核心开发者 banteg 推出 1:100 万拆分 YFI 代币的新项目 Woofy
  8. flutter初体验之基础控件知识
  9. [转]windows 2003 开启远程桌面
  10. java基于Springboot+vue的零食销售购物商城 elementui 前后端分离
  11. 模拟人生4修改服务器,模拟人生4 全秘籍、作弊码一览 模拟人生4修改方法汇总...
  12. 安装qq提示IE浏览器版本低
  13. Matlab透视变换
  14. 从零开始的MySQL教程——下
  15. 热搜第一除了本科生月均5千工资,还告诉了我们什么?
  16. 【单片机毕业设计】【mcuclub-jj-050】基于单片机的门禁的设计
  17. 记录kettle spoon.bat 无法启动 (系统找不到指定的路径。) 只有无限接近死亡 才能领悟真滴
  18. 用Delphi开发视频聊天软件
  19. 【过关斩将7】面试谈薪资时,HR压价怎么办?
  20. 智慧电厂信息系统设计方案

热门文章

  1. 你也许只使用到了 VS Code 20% 的功能
  2. 【每日一算法】二叉树的最小深度
  3. linux下彻底杀死ngnix进程方法
  4. Java关键字和保留字
  5. Servlet 请求的转发
  6. ACMNO.27 Python的两行代码解决 C语言-字符逆序 写一函数。使输入的一个字符串按反序存放,在主函数中输入输出反序后的字符串。 输入 一行字符 输出 逆序后的字符串
  7. Image-to-Image的论文汇总
  8. LSTM模型与前向反向传播算法
  9. 主成分分析(PCA):通过图像可视化深入理解
  10. 基于三维卷积神经网络的RGB-D显著目标检测