您当前的位置:首页 > 电脑百科 > 数据库 > Redis

基于Redis实现的分布式锁知识点总结

时间:2020-11-09 17:05:29  来源:  作者:

应用场景

分布式系统中,面对高并发场景,又对数据一致性有一定要求的情况下,使用分布式锁。例如商城中下单扣库存这种情况。

解决方案

基于数据库

例如:

select * from mall_spu where id=111 for update

例如:专门建一张表用来实现。例如以类名、方法名、数据ID作为唯一主键,org.leo.mall.order.OrderServer.addOrder.skuId.111,方法执行的时候,如果能插入成功,代表拿到锁,如果报主键冲突,则拿锁失败。


基于Zookeeper

以类、方法、数据ID作为目录,请求取顺序节点&节点列表,如果自己的节点最小,说明拿锁成功。而且还可以通过watch,在锁释放的时候重新拿锁。因为是临时锁,所以主动释放,或者session失效都可以释放锁,避免死锁产生。

性能差点,因为Zookeeper的操作都在主节点上。


基于redis

本文主要讲讲应用的一些变革。

1、加锁。

原来的做法是:

public static boolean getLock(String key,int expireTime){
 Long result=RedisClient.setnx(key,"");
 if(result!=1){return false}
 RedisClient.expire(key,expireTime);
 return true;
}

setnx加锁,成功后用expire加上超时时间。

问题在于:sennx和expire不是原子操作,万一expire的时候崩了,这条命令永远不过期了。

所以后来基于Redis的升级,有了下面正确的加锁方法:

public static boolean getLock(String key,String requestId,int expireTime){
 String result=RedisClient.set(key,requestId,"NX","PK",expireTime);
 if(result.equals("OK")){return true;}
 return false;
}

其实就是用Redis提供的一条set命令,替代了前面的setnx、expire两条命令,保证了原子性。

NX是指Key不存在就新增。PX是指设置超时时间。

requestId是为了后面解锁用。

2、解锁

解锁看着最简单,其实蛮复杂。

脑子里第一想法就是:

public static void releaseLock(String key){
 RedisClient.del(key);
}

这个危险性在于任何人都可以解锁!比如A请求加了锁:spu_id_111。B请求也要对111进行操作,一看锁被占了,直接del,然后自己拿锁——虽然在程序开发上讲,没有哪个傻子会这么干!!

所以这才有了第二种做法:

A请求加锁的时候,通过UUID、Random等方法生成随机数requestId。

public static void releaseLock(String key,String requestId){
  String result=RedisClient.get(key);//步骤1
  if(result.equals(requestId)){//步骤2
   //二者相等,说明加解锁的请求是同一个
   RedisClient.del(key);//步骤3
 }
}

看似很严谨,但是问题出在哪呢?还是出在操作不是原子性上。

A请求执行步骤1、2完毕,还未执行步骤2时,锁过期了,自动解锁!这时B请求加锁必然成功,而A请求继续执行步骤3,把B请求的锁给删了。

正确的做法如下:

public static boolean releaseLock(String key,String requestId){
 String luaCommand="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
 List<String> keyList=Lists.newArrayList(key);//这里我用的是Guava
List<String> valList=Lists.newArrayList(requestId);
 Object result=RedisClient.eval(luaCommand,keyList,valList);
 if(result.equals(1L)){return true;}
 return false;
}

利用的就是Redis通过eval命令执行LUA脚本是原子性的特性。


再然后就是使用Redisson实现了,这个适用于集群部署的Redis。

我在实际使用Redis分布式锁的时候遇到过一种情况。使用分布式锁后,要调用第三方接口,从而导致整个流程时间偏长,锁过期的情况下还没有执行完,当时的处理方式是加大了过期时间。

如果使用Redisson,因为有看门狗机制,就很好地解决了这个问题。看门狗会定时去检查,如果请求实例还在则自动去延长超时时间。不过这带来的问题一定是性能的下降,所以当时我们还是采用了粗暴的延长设置过期时间来解决此类问题。

Redisson也是个可重入锁,因为锁的内容除了key、实例ID之外还有数字Value,这样一来同样的实例多次拿锁,Value+1,释放锁,Value-1即可。



Tags:Redis   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Tags: Redis  点击:(16)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  Tags: Redis  点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  Tags: Redis  点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  Tags: Redis  点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  Tags: Redis  点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  Tags: Redis  点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  Tags: Redis  点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  Tags: Redis  点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  Tags: Redis  点击:(77)  评论:(0)  加入收藏
Nginx来限制访问控制的方法有多种,nginx主要有2个模块控制,但是那些不支持自定义,非常死,在大多数场景下并不实用。今天分享一个:利用openresty+lua+redis 实现封杀频繁恶意访问I...【详细内容】
2021-08-12  Tags: Redis  点击:(118)  评论:(0)  加入收藏
▌简易百科推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Java识堂    Tags:Redis   点击:(16)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  富集云科技有限公司    Tags:Redis   点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  弈秋的美好生活    Tags:redis   点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  川石信息    Tags:Redis   点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  JavaEdge    Tags:Redis   点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  faesuite    Tags:Redis   点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  语霖    Tags:Redis   点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  深夜敲代码    Tags:Redis   点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  小李说IT    Tags:Redis   点击:(77)  评论:(0)  加入收藏
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据...【详细内容】
2021-07-30  随便t    Tags:缓存穿透   点击:(90)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条