展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > Redis中的分布式锁如何实现

Redis中的分布式锁如何实现

Redis Assistant
Redis Assistant

Redis Assistant 是一款为Redis设计的高效可视化管理与监控工具,提供多种操作模式,高效处理服务器数据。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

¥19
Windows/macOS/Linux 2026-08-22
正版软件 Windows macOS软件 Linux 数据库管理工具
立即下载

什么是分布式锁说到Redis,我们第一想到的功能就是可以缓存数据,除此之外,Redis因为单进程、性能高的特点,它还经常被用于做分布式锁。锁我们都知道,在程序中的作用就是同步工具,保证共享资源在同一时刻只能被一个线程访问,Java中的锁我们都很熟悉了,像synchronized、Lock都是我们经常使用的,但是Java的锁只能保证单机的时候有效,分布式集群环境就无能为力了,这个时候我们就需要用到分布式锁。分布式锁,顾名思义,就是分布式项目开发中用到的锁,可以用来控制分布式系统之间同步访问共享资源,一般来说

本文目录

    Redis中的分布式锁如何实现

    什么是分布式锁

    说到Redis,我们第一想到的功能就是可以缓存数据,除此之外,Redis因为单进程、性能高的特点,它还经常被用于做分布式锁。

    锁我们都知道,在程序中的作用就是同步工具,保证共享资源在同一时刻只能被一个线程访问,Java中的锁我们都很熟悉了,像synchronized 、Lock都是我们经常使用的,但是Java的锁只能保证单机的时候有效,分布式集群环境就无能为力了,这个时候我们就需要用到分布式锁。

    分布式锁,顾名思义,就是分布式项目开发中用到的锁,可以用来控制分布式系统之间同步访问共享资源,一般来说,分布式锁需要满足的特性有这么几点:

    1、互斥性:在任何时刻,对于同一条数据,只有一台应用可以获取到分布式锁;

    2、高可用性:在分布式场景下,一小部分服务器宕机不影响正常使用,这种情况就需要将提供分布式锁的服务以集群的方式部署;

    3、防止锁超时:如果客户端没有主动释放锁,服务器会在一段时间之后自动释放锁,防止客户端宕机或者网络不可达时产生死锁;

    4、独占性:加锁解锁必须由同一台服务器进行,也就是锁的持有者才可以释放锁,不能出现你加的锁,别人给你解锁了;

    业界里可以实现分布式锁效果的工具很多,但操作无非这么几个:加锁、解锁、防止锁超时。

    既然本文说的是Redis分布式锁,那我们理所当然就以Redis的知识点来延伸。

    实现锁的命令

    先介绍下Redis的几个命令,

    1、SETNX,用法是SETNX key value

    SETNX是『 SET if Not eXists』(如果不存在,则 SET)的简写,设置成功就返回1,否则返回0。

    Redis中的分布式锁如何实现

    setnx用法

    可以看出,当把key为lock的值设置为"Java"后,再设置成别的值就会失败,看上去很简单,也好像独占了锁,但有个致命的问题,就是key没有过期时间,这样一来,除非手动删除key或者获取锁后设置过期时间,不然其他线程永远拿不到锁。

    既然这样,我们给key加个过期时间总可以吧,直接让线程获取锁的时候执行两步操作:

    `SETNX Key 1`
    `EXPIRE Key Seconds`

    这个方案也有问题,因为获取锁和设置过期时间分成两步了,不是原子性操作,有可能获取锁成功但设置时间失败,那样不就白干了吗。

    不过也不用急,这种事情Redis官方早为我们考虑到了,所以就引出了下面这个命令

    2、SETEX,用法SETEX key seconds value

    将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在,SETEX 命令将覆写旧值。

    这个命令类似于以下两个命令:

    `SET key value`
    `EXPIRE key seconds  # 设置生存时间`

    这两步动作是原子性的,会在同一时间完成。

    Redis中的分布式锁如何实现

    setex用法

    3、PSETEX ,用法PSETEX key milliseconds value

    这个命令和SETEX命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像SETEX命令那样,以秒为单位。

    不过,从Redis 2.6.12 版本开始,SET命令可以通过参数来实现和SETNX、SETEX、PSETEX 三个命令相同的效果。

    就比如这条命令

    `SET key value NX EX seconds`

    加上NX、EX参数后,效果就相当于SETEX,这也是Redis获取锁写法里面最常见的。

    怎么释放锁

    释放锁的命令就简单了,直接删除key就行,但我们前面说了,因为分布式锁必须由锁的持有者自己释放,所以我们必须先确保当前释放锁的线程是持有者,没问题了再删除,这样一来,就变成两个步骤了,似乎又违背了原子性了,怎么办呢?

    不慌,我们可以用lua脚本把两步操作做拼装,就好像这样:

    `if redis.call("get",KEYS[1]) == ARGV[1]`
    `then`
     `return redis.call("del",KEYS[1])`
    `else`
     `return 0`
    `end`

    KEYS[1]是当前key的名称,ARGV[1]可以是当前线程的ID(或者其他不固定的值,能识别所属线程即可),这样就可以防止持有过期锁的线程,或者其他线程误删现有锁的情况出现。

    代码实现

    知道了原理后,我们就可以手写代码来实现Redis分布式锁的功能了,因为本文的目的主要是为了讲解原理,不是为了教大家怎么写分布式锁,所以我就用伪代码实现了。

    首先是redis锁的工具类,包含了加锁和解锁的基础方法:

    `public class RedisLockUtil {`
     `private String LOCK_KEY = "redis_lock";`
     `// key的持有时间,5ms`
     `private long EXPIRE_TIME = 5;`
     `// 等待超时时间,1s`
     `private long TIME_OUT = 1000;`
     `// redis命令参数,相当于nx和px的命令合集`
     `private SetParams params = SetParams.setParams().nx().px(EXPIRE_TIME);`
     `// redis连接池,连的是本地的redis客户端`
     `JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);`
     `/**`
     `* 加锁`
     `*`
     `* @param id`
     `*            线程的id,或者其他可识别当前线程且不重复的字段`
     `* @return`
     `*/`
     `public boolean lock(String id) {`
     `Long start = System.currentTimeMillis();`
     `Jedis jedis = jedisPool.getResource();`
     `try {`
     `for (;;) {`
     `// SET命令返回OK ,则证明获取锁成功`
     `String lock = jedis.set(LOCK_KEY, id, params);`
     `if ("OK".equals(lock)) {`
     `return true;`
     `}`
     `// 否则循环等待,在TIME_OUT时间内仍未获取到锁,则获取失败`
     `long l = System.currentTimeMillis() - start;`
     `if (l >= TIME_OUT) {`
     `return false;`
     `}`
     `try {`
     `// 休眠一会,不然反复执行循环会一直失败`
     `Thread.sleep(100);`
     `} catch (InterruptedException e) {`
     `e.printStackTrace();`
     `}`
     `}`
     `} finally {`
     `jedis.close();`
     `}`
     `}`
     `/**`
     `* 解锁`
     `*`
     `* @param id`
     `*            线程的id,或者其他可识别当前线程且不重复的字段`
     `* @return`
     `*/`
     `public boolean unlock(String id) {`
     `Jedis jedis = jedisPool.getResource();`
     `// 删除key的lua脚本`
     `String script = "if redis.call('get',KEYS[1]) == ARGV[1] then" + "   return redis.call('del',KEYS[1]) " + "else"`
     `+ "   return 0 " + "end";`
     `try {`
     `String result =`
     `jedis.eval(script, Collections.singletonList(LOCK_KEY), Collections.singletonList(id)).toString();`
     `return "1".equals(result);`
     `} finally {`
     `jedis.close();`
     `}`
     `}`
    `}`

    具体的代码作用注释已经写得很清楚了,然后我们就可以写一个demo类来测试一下效果:

    `public class RedisLockTest {`
     `private static RedisLockUtil demo = new RedisLockUtil();`
     `private static Integer NUM = 101;`
     `public static void main(String[] args) {`
     `for (int i = 0; i < 100; i++) {`
     `new Thread(() -> {`
     `String id = Thread.currentThread().getId() + "";`
     `boolean isLock = demo.lock(id);`
     `try {`
     `// 拿到锁的话,就对共享参数减一`
     `if (isLock) {`
     `NUM--;`
     `System.out.println(NUM);`
     `}`
     `} finally {`
     `// 释放锁一定要注意放在finally`
     `demo.unlock(id);`
     `}`
     `}).start();`
     `}`
     `}`
    `}`

    我们创建100个线程来模拟并发的情况,执行后的结果是这样的:

    Redis中的分布式锁如何实现

    代码执行结果

    可以看出,锁的效果达到了,线程安全是可以保证的。

    当然,上面的代码只是简单的实现了效果,功能肯定是不完整的,一个健全的分布式锁要考虑的方面还有很多,实际设计起来不是那么容易的。

    我们的目的只是为了学习和了解原理,手写一个工业级的分布式锁工具不现实,也没必要,类似的开源工具一大堆(Redisson),原理都差不多,而且早已经过业界同行的检验,直接拿来用就行。

    虽然功能是实现了,但其实从设计上来说,这样的分布式锁存在着很大的缺陷,这也是本篇文章想重点探讨的内容。

    分布式锁的缺陷

    一、客户端长时间阻塞导致锁失效问题

    客户端1得到了锁,因为网络问题或者GC等原因导致长时间阻塞,然后业务程序还没执行完锁就过期了,这时候客户端2也能正常拿到锁,可能会导致线程安全的问题。

    Redis中的分布式锁如何实现

    客户端长时间阻塞

    那么该如何防止这样的异常呢?我们先不说解决方案,介绍完其他的缺陷后再来讨论。

    二、redis服务器时钟漂移问题

    如果redis服务器的机器时钟发生了向前跳跃,就会导致这个key过早超时失效,比如说客户端1拿到锁后,key的过期时间是12:02分,但redis服务器本身的时钟比客户端快了2分钟,导致key在12:00的时候就失效了,这时候,如果客户端1还没有释放锁的话,就可能导致多个客户端同时持有同一把锁的问题。

    三、单点实例安全问题

    如果redis是单master模式的,当这台机宕机的时候,那么所有的客户端都获取不到锁了,为了提高可用性,可能就会给这个master加一个slave,但是因为redis的主从同步是异步进行的,可能会出现客户端1设置完锁后,master挂掉,slave提升为master,因为异步复制的特性,客户端1设置的锁丢失了,这时候客户端2设置锁也能够成功,导致客户端1和客户端2同时拥有锁。

    为了解决Redis单点问题,redis的作者提出了RedLock算法。

    RedLock算法

    该算法的实现前提在于Redis必须是多节点部署的,可以有效防止单点故障,具体的实现思路是这样的:

    1、获取当前时间戳(ms);

    2、先设定key的有效时长(TTL),超出这个时间就会自动释放,然后client(客户端)尝试使用相同的key和value对所有redis实例进行设置,每次链接redis实例时设置一个比TTL短很多的超时时间,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。

    比如:TTL(也就是过期时间)为5s,那获取锁的超时时间就可以设置成50ms,所以如果50ms内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁;

    3、client通过获取所有能获取的锁后的时间减去第一步的时间,还有redis服务器的时钟漂移误差,然后这个时间差要小于TTL时间并且成功设置锁的实例数>= N/2 + 1(N为Redis实例的数量),那么加锁成功

    比如TTL是5s,连接redis获取所有锁用了2s,然后再减去时钟漂移(假设误差是1s左右),那么锁的真正有效时长就只有2s了;

    4、如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例。

    根据这样的算法,我们假设有5个Redis实例的话,那么client只要获取其中3台以上的锁就算是成功了,用流程图演示大概就像这样:

    Redis中的分布式锁如何实现

    key有效时长

    好了,算法也介绍完了,从设计上看,毫无疑问,RedLock算法的思想主要是为了有效防止Redis单点故障的问题,而且在设计TTL的时候也考虑到了服务器时钟漂移的误差,让分布式锁的安全性提高了不少。

    但事实真的是这样吗?反正我个人的话感觉效果一般般,

    首先第一点,我们可以看到,在RedLock算法中,锁的有效时间会减去连接Redis实例的时长,如果这个过程因为网络问题导致耗时太长的话,那么最终留给锁的有效时长就会大大减少,客户端访问共享资源的时间很短,很可能程序处理的过程中锁就到期了。而且,锁的有效时间还需要减去服务器的时钟漂移,但是应该减多少合适呢,要是这个值设置不好,很容易出现问题。

    然后第二点,这样的算法虽然考虑到用多节点来防止Redis单点故障的问题,但但如果有节点发生崩溃重启的话,还是有可能出现多个客户端同时获取锁的情况。

    假设一共有5个Redis节点:A、B、C、D、E,客户端1和2分别加锁

    • 客户端1成功锁住了A,B,C,获取锁成功(但D和E没有锁住)。

    • 节点C的master挂了,然后锁还没同步到slave,slave升级为master后丢失了客户端1加的锁。

    • 客户端2这个时候获取锁,锁住了C,D,E,获取锁成功。

    这样,客户端1和客户端2就同时拿到了锁,程序安全的隐患依然存在。除此之外,如果这些节点里面某个节点发生了时间漂移的话,也有可能导致锁的安全问题。

    所以说,虽然通过多实例的部署提高了可用性和可靠性,但RedLock并没有完全解决Redis单点故障存在的隐患,也没有解决时钟漂移以及客户端长时间阻塞而导致的锁超时失效存在的问题,锁的安全性隐患依然存在。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。

    即将离开本站
    您即将前往第三方网站,请确认是否继续?