深入理解redis分布式锁哈喽,大家好,我是指北君。
本篇文件我们来介绍如何redis实现分布式锁的演进过程,以及为什么不能直接用setnx实现分布式锁。
1、分布式锁简介分布式锁是控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
业界流行的分布式锁实现,一般有这3种方式:
基于数据库实现的分布式锁基于redis实现的分布式锁基于zookeeper实现的分布式锁这里主要介绍如何通过 redis 来实现分布式锁。在介绍 redis 分布式锁之前,我们首先介绍一下实现redis 分布式锁的关键命令。
2、setnxsetnx key value
setnx(set if not exists) 命令在指定的 key 不存在时,为 key 设置指定的值。
设置成功,返回 1 。设置失败,返回 0 。
ps:redis 官方是不推荐基于 setnx 命令来实现分布式锁的,因为会存在很多问题,
①、单点问题。比如:
1、客户端a 从master拿到锁lock012、master正要把lock01同步(redis的主从同步通常是异步的)给slave时,突然宕机了,导致lock01没同步给slave3、主从切换,slave节点被晋级为master节点4、客户端b到master拿lock01照样能拿到。这样必将导致同一把锁被多人使用。②、锁的高级用法,比如读写锁、可重入锁等等,setnx 都比较难实现。
这里先介绍基于 sentnx 实现的分布式锁,后面会介绍官方推荐的基于 redisson 来实现分布式锁。
3、redis-分布式锁-阶段1接到上文,查询三级分类数据,如果我们部署了多个商品服务,然后多个线程同时去获取三级分类数据,如果不加分布式锁,就会导致,每一个部署的商品服务第一次查询都会走 db。
public map getcatelogjsonwithredislock() throws interruptedexception { // 一、获取分布式锁 boolean lock = stringredistemplate.opsforvalue().setifabsent(lock, 111); if(lock){ // true 表示加锁成功,执行相关业务 map datafromdb = getdatafromdb(); stringredistemplate.delete(lock); return datafromdb; }else{ system.out.println(获取分布式锁失败...等待重试...); //加锁失败...重试机制 //休眠一百毫秒 try { timeunit.milliseconds.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } //自旋的方式 return getcatelogjsonwithredislock(); }}
4、redis-分布式锁-阶段2设置锁自动过期
public map getcatelogjsonwithredislock() throws interruptedexception { // 一、获取分布式锁 boolean lock = stringredistemplate.opsforvalue().setifabsent(lock, 111); if(lock){ // true 表示加锁成功,执行相关业务 // 设置过期时间 stringredistemplate.expire(lock,30,timeunit.seconds); map datafromdb = getdatafromdb(); stringredistemplate.delete(lock); return datafromdb; }else{ system.out.println(获取分布式锁失败...等待重试...); //加锁失败...重试机制 //休眠一百毫秒 try { timeunit.milliseconds.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } //自旋的方式 return getcatelogjsonwithredislock(); }}
5、redis-分布式锁-阶段3setnx 命令和过期时间保证原子性。
public map getcatelogjsonwithredislock() throws interruptedexception { // 一、获取分布式锁 boolean lock = stringredistemplate.opsforvalue().setifabsent(lock, 111,30,timeunit.seconds); if(lock){ // true 表示加锁成功,执行相关业务 // 设置过期时间 //stringredistemplate.expire(lock,30,timeunit.seconds); map datafromdb = getdatafromdb(); stringredistemplate.delete(lock); return datafromdb; }else{ system.out.println(获取分布式锁失败...等待重试...); //加锁失败...重试机制 //休眠一百毫秒 try { timeunit.milliseconds.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } //自旋的方式 return getcatelogjsonwithredislock(); }}
6、redis-分布式锁-阶段4保证删除的是自己的锁。
public map getcatelogjsonwithredislock() throws interruptedexception { // 一、获取分布式锁 string uuid = uuid.randomuuid().tostring(); boolean lock = stringredistemplate.opsforvalue().setifabsent(lock, uuid,30,timeunit.seconds); if(lock){ // true 表示加锁成功,执行相关业务 // 设置过期时间 //stringredistemplate.expire(lock,30,timeunit.seconds); map datafromdb = getdatafromdb(); string lockvalue = stringredistemplate.opsforvalue().get(lock); if(uuid.equals(lockvalue)){ stringredistemplate.delete(lock); } return datafromdb; }else{ system.out.println(获取分布式锁失败...等待重试...); //加锁失败...重试机制 //休眠一百毫秒 try { timeunit.milliseconds.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } //自旋的方式 return getcatelogjsonwithredislock(); }}
7、redis-分布式锁-阶段5通过lua脚本保证删除锁和判断锁两个操作原子性
public map getcatelogjsonwithredislock(){ // 一、获取分布式锁 string uuid = uuid.randomuuid().tostring(); boolean lock = stringredistemplate.opsforvalue().setifabsent(lock, uuid,30,timeunit.seconds); if (lock) { system.out.println(获取分布式锁成功...); map datafromdb = null; try { //加锁成功...执行业务 datafromdb = getdatafromdb(); } finally { string script = if redis.call('get', keys[1]) == argv[1] then return redis.call('del', keys[1]) else return 0 end; //删除锁 stringredistemplate.execute(new defaultredisscript(script, long.class), arrays.aslist(lock), uuid); } //先去redis查询下保证当前的锁是自己的 //获取值对比,对比成功删除=原子性 lua脚本解锁 // string lockvalue = stringredistemplate.opsforvalue().get(lock); // if (uuid.equals(lockvalue)) { // //删除我自己的锁 // stringredistemplate.delete(lock); // } return datafromdb; }else{ system.out.println(获取分布式锁失败...等待重试...); //加锁失败...重试机制 //休眠一百毫秒 try { timeunit.milliseconds.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } //自旋的方式 return getcatelogjsonwithredislock(); }}这也是分布式锁的最终模式,需要保证两个点:加锁【设置锁+过期时间】和删除锁【判断+删除】原子性。
小米10钢化膜疑似曝光 或将是小米数字系列首款曲面屏机型
海信U8E电视评测 令人激动的画质表现
智能显示屏有多厉害,可以撑起智能家居的半边天
意料中的爆冷!一年时间从第一跌出前五,这家手机巨头如何涅槃重生?
荣耀magic能否引领互联网手机2.0时代的到来?
深入理解redis分布式锁
博世与戴姆勒正在持续深化合作,推动加快城市道路的完全自动驾驶汽车的发展
晶诠科技获得MIPS公司IP内核多项授权开发USB2.0 O
血糖仪设计在人机上提高产品的操作简便性和舒适度
关于智慧农业项目的建设,都由哪些硬件设备组成
顶尖电子博览会预告,特殊时期的特殊规划
BASIC语言概述及特点
魅蓝Note6发布会在即:处理器成迷,是联发科还是高通?魅蓝首款双摄手机买999?
我国电子产业现状如何,该如何持续发展?
使用Bolt WiFi制作温度监测警报器
物理学
基于一种DeFi的顶级区块链平台介绍
创想三维以客户至上为宗旨 打造全球3D打印机行业一流服务品牌
利用GPU来找寻圣诞老人的踪迹
石墨烯被称为黑色的金子,石墨烯又在电池领域有了重大突破