分布式锁概念

分布式锁其实就是,控制分布式系统的不同进程共同访问共享资源的一种锁的实现。如果不同系统或同一个系统的不同主机去访问一个共享的临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
分布式锁应该具备以下条件:

  1. 互斥性:任意时刻,只允许一个客户端访问。
  2. 锁超时:持有锁超时,可以释放,避免资源浪费,也可以防止死锁。
  3. 可重入性:一个线程获取锁之后,还可以再次请求加锁。
  4. 高可用和高性能:加锁和释放锁的开销要尽可能的低,同时保证高可用,防止分布式锁失效。
  5. 安全性:锁只能被持有的客户端删除,不能被其他客户端删除。

分布式锁实现方式

1,基于数据库锁
2,基于Redis锁
3,基于Zookeeper

Redis分布式锁实现方案

本文以基于Redis来实现分布式锁,描述多个实现方案,并分析利弊。

方案一:SETNX + EXPIRE

Redis中有一个 SETNX 命令,命令格式是 SETNX key value,如果key不存在,则SETNX返回1,如果已存在,则返回0。
那么可以使用 SETNX + EXPIRE命令,即SETNX抢到锁,在用EXPIRE给锁一个过期时间,防止锁忘记释放或者服务奔溃而没有释放锁。
伪代码如下:

if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁expire(key_resource_id,100); //设置过期时间try {do something  //业务请求}catch(){}finally {jedis.del(key_resource_id); //释放锁}
}

这个方案的问题是,setnx和expire两个命令操作不是原子性的,那么有可能在setnx加完锁之后,进程发生异常等原因,设置过期时间还没有执行,那么就会导致这个锁一直得不到释放,其他请求就会一直在等待,造成死锁问题。

方案二:使用lua脚本(包含SETNX + EXPIRE两条指令)

针对方案一的问题,那么可以使用lua脚本来保证原子性(包含SETNXX + EXPIRE)。
lua脚本如下:

if redis.call('setnx',KEYS[1],ARGV[1]) == 1 thenredis.call('expire',KEYS[1],ARGV[2])
elsereturn 0
end;

加锁代码如下:

 String lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" +" redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end";
Object result = jedis.eval(lua_scripts, Collections.singletonList(key_resource_id), Collections.singletonList(values));
//判断是否成功
return result.equals(1L);

方案三:SET的扩展命令(SET NX EX PX)

我们可以使用lua脚本来保证SETNX + EXPIRE两条命令的原子性,也可以使用Redis的SET指令扩展参数来实现。

语法:SET key value NX|XX EX|PX  expire_time
参数:
NX:只有健key不存在的时候,才会去设置健key的值
PX:只有健key存在的时候,才会去设置健key的值
EX:设置健key的过期时间,单位为秒
PX:设置健key的过期时间,单位为毫秒
expire_time:过期时间,整数类型

伪代码demo如下:

if(jedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1){ //加锁try {do something  //业务处理}catch(){}finally {jedis.del(key_resource_id); //释放锁}
}

方案二和方案三的还可能存在问题:
问题1,锁过期释放了,业务还没有执行完。比如a线程拿到锁并执行业务代码,但是过期时间到了,业务代码还没有执行完成,那么b线程进来,就能拿到锁,此时就不是同步串行执行的。
问题2,锁被别的线程误删。比如a线程拿到锁,然后准备去释放锁,但有可能过期时间到了,b线程进来拿到锁,此时a线程去释放锁,就会把b线程的锁给释放带,但b线程的业务代码还没有执行完成,后续又有新线程来拿锁,导致问题的发生。

方案四:SET NX EX|PX + 校验唯一随机值,再删除

既然锁可能被别的线程误删除,那么给value值设置一个标记当前线程的唯一随机值,在删除的时候校验一下。

加锁和释放锁的参考代码如下:

/*** 尝试获取分布式锁* @param lockKey 锁* @param requestId 请求标识,唯一ID, 可以使用UUID.randomUUID().toString();* @param expireTime 超期时间,毫秒* @return 是否获取成功*/
public  boolean redisLockByKey(String lockKey, String requestId, int expireTime) {Jedis jedis = jedisPool.getResource();try {String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (LOCK_SUCCESS.equals(result)) {return true;}return false;} finally {jedis.close();}
}/*** 释放分布式锁* @param lockKey 锁* @param requestId 请求标识* @return 是否释放成功*/
public  boolean redisUnlockByKey(String lockKey, String requestId) {Jedis jedis = jedisPool.getResource();try {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;} finally {jedis.close();}}

方案四解决了误删除的问题,但还是没有解决【锁过期释放,业务代码还没有执行完】的问题。

方案五:Redisson框架

其实以上的方案,还只是基于单机版的Redis来讨论的,但生产环境大多都是集群模式部署的。

如果线程a在Redis的master节点上拿到锁,但是加锁的key还没有同步到slave节点。恰好此时,master节点发生故障,一个slave节点会升级为master,这时候线程b就能拿到同个key的锁,而线程a也同样拿到锁,这样就不能保证安全性了。

针对这个问题,Redis作者antirez提出一个高级的分布式锁算法,Redlock。
Redlock核心思想是这样的:

搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。

Redlock的实现步骤如下:

1.获取当前时间,以毫秒为单位。2.按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。3.客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)4,如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。5,如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。

Redisson框架实现了Redlock版本的锁。

使用Redisson框架就可以解决以上方案的问题,并且是基于集群模式的,是一个目前较完美的分布式锁解决方案。

Redis分布式锁一文全攻略相关推荐

  1. 分布式技术学习路线全攻略

    不管你是高级程序员,还是一名架构师,我想你早已经对分布式这个词耳熟能详了.打开各大招聘网站,不难发现,在招聘JD里,一线互联网公司对于候选人的要求中都有分布式系统设计这一关键词.为什么会这样呢? 我觉 ...

  2. 阿里Redis最全面试全攻略,读完这个就可以和阿里面试官好好聊聊

    什么是Redis及其重要性? Redis是一个使用ANSI C编写的开源.支持网络.基于内存.可选持久化的高性能键值对数据库. Redis的之父是来自意大利的西西里岛的Salvatore Sanfil ...

  3. Redis分布式锁(图解 - 秒懂 - 史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  4. 一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)(转)

    近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术,常用的分布式实现方式为Redis,Z ...

  5. 一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)

    近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术,常用的分布式实现方式为Redis,Z ...

  6. getset原子性 redis_一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)

    各个版本的Redis分布式锁 V1.0 V1.1 基于[GETSET] V2.0 基于[SETNX] V3.0 V3.1 分布式Redis锁:Redlock 总结 <Netty 实现原理与源码解 ...

  7. 一个项目部署多个节点会导致锁失效么_一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)...

    各个版本的Redis分布式锁 V1.0 V1.1 基于[GETSET] V2.0 基于[SETNX] V3.0 V3.1 分布式Redis锁:Redlock 总结 <Netty 实现原理与源码解 ...

  8. Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案)

    前言 在某些场景中,多个进程必须以互斥的方式独占共享资源,这时用分布式锁是最直接有效的. 随着技术快速发展,数据规模增大,分布式系统越来越普及,一个应用往往会部署在多台机器上(多节点),在有些场景中, ...

  9. 爽文,Redis分布式锁的实现和原理

    为什么需要分布式锁 我们知道,当多个线程并发操作某个对象时,可以通过synchronized来保证同一时刻只能有一个线程获取到对象锁进而处理synchronized关键字修饰的代码块或方法.既然已经有 ...

最新文章

  1. Java 8 失宠!开发人员向 Java 11 转移...
  2. python编译安装pyaudio
  3. boost::fibers::unbuffered_channel用法的测试程序
  4. 如何删除写保护的文件_如何找回已删除或永久删除的Office Excel文件
  5. 【Linux系统编程】POSIX无名信号量
  6. SqlHelper简单实现(通过Expression和反射)4.对象反射Helper类
  7. LINUX 内核代码备忘录
  8. 左侧固定右侧自动填充_ai怎么填充颜色?在ai里怎么填充颜色?
  9. Java开源内容管理CMS系统J4CMS的几个样式
  10. mysql call_关于Mysql “CALL语句”
  11. Linux学习新篇章C高级:day1
  12. aliddns ipv6_linux系统下配置阿里DDNS(IPv6)
  13. 如何用python获取沪深300历年成分股的行情数据
  14. win10中 有道词典不能联网/发音解决方法
  15. windows bat脚本 启动和停止程序
  16. 目标检测简介和滑动窗口
  17. 书摘—拆掉思维里的墙
  18. Gin框架组合(Zap、lumberjack、ini)使用手册
  19. 6个爱好编程者可以参与以促进放松
  20. 微信MAC最新版3.1.0支持发朋友圈了

热门文章

  1. vue从入门到放弃(五)
  2. 虚拟实验工场大学计算机实验报告答案,虚拟现实技术实验报告一.doc
  3. 2023年端午节高速免费吗?假期待办事项用手机定时提醒
  4. 协同控制笔记1——基础介绍及部分定义定理
  5. android shell检查是否锁屏_锁屏状态下的华为手机不显示消息?这样设置!
  6. 华为手机计算机删除怎么恢复出厂设置,华为手机恢复出厂设置能彻底清除垃圾吗? 恢复出厂怎么操作...
  7. 计算机管理服务所有任务,电脑Windows任务管理器显示不全的5个解决方法
  8. 网站根据不同时间段php输出不同的问候语
  9. MAC系统上设置华为手机的调试模式
  10. 在Win11中Solidworks 2016安装失败因为VC++2005安装失败的解决办法