Redis实现分布式锁的几种方案 (redis实现延迟消息队列)

教程大全 2025-07-20 15:56:19 浏览

1.前言

对于Redis实现分布式锁的几种方案这个话题,展开之前我想先简单聊聊什么是分布式锁,分布式锁的使用场景,除了Redis外还有什么技术实现分布式锁等一系列内容。

redis实现延迟消息队列

1.1分布式锁

说大一点,就是在现在发展越来越迅速的大背景下,去中心化分布式系统越来越普及,在我们实际的生产开发当中,有一种不可避免的场景就是多个进程互斥的对其资源的使用,为了保证数据不重复,要求在同一时刻,同一任务只在一个节点上运行,且保证在多进程下的数据安全,分布式锁就十分重要了。

1.2分布式锁的几种方案

方式有很多种,根据技术角度的不同

有基于MySQL的方式,通过表的唯一索引,通过insert和delete就可以实现加锁和解锁的效果;

有基于zookeeper的方式,通过创建临时有序节点,判断创建的节点序号是否最小。若是,则表示获取到锁,不是,则watch /lock目录下序号比自身小的前一个节点,解锁只需要删除节点;

有基于Redis的方式。通过执行setnx,若成功再执行expire添加过期时间的方式加锁,解锁执行delete命令。

方式有很多,不一一列举了。

1.3Redis分布式锁需要满足的条件

2.Redis实现分布式锁的几种方案

可以通过以下方式实现(包括但不限于):

简单来说说,用Java代码演示:

2.1 SETNX + EXPIRE

setnx(SET IF NOT EXISTS)+expire命令。先用setnx来抢锁,如果抢到锁,再用expire给锁设置一个过期时间,这样持有锁超时时释放锁,防止锁忘记释放。但此时setnx和expire两个命令无法保证原子性,例如:

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

2.2 SETNX + value(系统时间+过期时间)

可以把过期时间放到setnx的value值里面。如果加锁失败,再拿出value值校验一下即可。加锁代码如下:

long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间String expiresStr = String.valueOf(expires);// 如果当前锁不存在,则加锁成功if (jedis.setnx(key_resource_id, expiresStr) == 1) {return true;} // 如果锁已经存在,获取锁的过期时间String currentValueStr = jedis.get(key_resource_id);// 如果获取到的过期时间,小于系统当前时间,表示已经过期if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {// 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间String oldValueStr = jedis.getSet(key_resource_id, expiresStr);if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁return true;}}//其他情况均返回加锁失败return false;

2.3 通过开源框架-Redisson

那么此时就要去想了,如果已经超过了加锁的过期时间,可是业务还没执行完成,这个时候怎么做呢?是把过期时间延长吗?显然不合理,可以通过开源框架-Redisson优化这个问题,简单来说,Redisson就是当一个线程获得锁以后,给该线程开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。假设两个线程争夺统一公共资源:线程A获取锁,并通过哈希算法选择节点,执行Lua脚本加锁,同时其看门狗机制会启动一个watch dog(后台线程),每隔10秒检查线程,如果线程A还持有锁,那么就会不断的延长锁key的生存时间。线程B获得锁失败,就会订阅解锁消息,当获取锁到剩余过期时间后,调用信号量方法阻塞住,直到被唤醒或等待超时。一旦线程A释放了锁,就会广播解锁消息。于是,解锁消息的监听器会释放信号量,获取锁被阻塞的线程B就会被唤醒,并重新尝试获取锁。

Redisson 支持单点模式、主从模式、哨兵模式、集群模式,假设现为单点模式:

//构造CONfigConfig config = new Config();config.useSingleServer().setAddress("redis://ip:port").setPassword("Password.~#").setDatabase(0);//构造RedissonClientRedissonClient redissonClient = Redisson.create(config);//获取锁实例RLock rLock = redissonClient.getLock(lockKey);try {//获取锁,waitTimeout为最大等待时间,超过这个值,则认为获取锁失败。leaseTime为锁的持有时间boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);if (res) {//业务块}} catch (Exception e) {}finally{//解锁rLock.unlock();}

3.小结

Redis的分布式锁实现方式有很多,这里不一一列举了,有机会再展开Lua脚本、分布式锁Redlock等内容。


scrapy使用redis的时候,redis需要进行一些设置吗

1.使用两台机器,一台是win10,一台是CentOS7,分别在两台机器上部署scrapy来进行分布式抓取一个网站7的ip地址为192.168.1.112,用来作为redis的master端,win10的机器作为的爬虫运行时会把提取到的url封装成request放到redis中的数据库:“dmoz:requests”,并且从该数据库中提取request后下载网页,再把网页的内容存放到redis的另一个数据库中“dmoz:items”从master的redis中取出待抓取的request,下载完网页之后就把网页的内容发送回master的redis5.重复上面的3和4,直到master的redis中的“dmoz:requests”数据库为空,再把master的redis中的“dmoz:items”数据库写入到mongodb中里的reids还有一个数据“dmoz:dupefilter”是用来存储抓取过的url的指纹(使用哈希函数将url运算后的结果),是防止重复抓取的!

如何理解而value对于Redis来说是一个字节数组,Redis并不知道value中存储的是什么

Redis不仅仅是一个简单的key-value内存数据库,Redis官网对自身的定义是“数据结构服务器”。 通过用心设计各种数据结构类型的数据存储,可以实现部分的数据查询功能。 因为在Redis的设计中,key是一切,对于Redis是可见的,而value对于Redis来说就是一个字节数组,Redis并不知道你的value中存储的是什么,所以要想实现比如‘select * From users where =shanghai’这样的查询,在Redis是没办法通过value进行比较得出结果的。 但是可以通过不同的数据结构类型来做到这一点。 比如如下的数据定义users:1 {name:Jack,age:28,location:shanghai}users:2 {name:Frank,age:30,location:beijing}users:location:shanghai [1]其中users:1 users:2 分别定义了两个用户信息,通过Redis中的hash数据结构,而users:location:shanghai 记录了所有上海的用户id,通过集合数据结构实现。 这样通过两次简单的Redis命令调用就可以实现我们上面的查询。 Jedis jedis = ();Set shanghaiIDs = (users:location:shanghai);//遍历该set//...//通过hgetall获取对应的user信息(users: + shanghaiIDs[0]);通过诸如以上的设计,可以实现简单的条件查询。 但是这样的问题也很多,首先需要多维护一个ID索引的集合,其次对于一些复杂查询无能为力(当然也不能期望Redis实现像关系数据库那样的查询,Redis不是干这的)。 但是Redis2.6集成了Lua脚本,可以通过eval命令,直接在RedisServer环境中执行Lua脚本,并且可以在Lua脚本中调用Redis命令。 其实,就是说可以让你用Lua这种脚本语言,对Redis中存储的key value进行操作,这个意义就大了,甚至可以将你们系统所需的各种业务写成一个个lua脚本,提前加载进入Redis,然后对于请求的响应,只需要调用一个个lua脚本就行。 当然这样说有点夸张,但是意思就是这样的。 比如,现在我们要实现一个‘所有age大于28岁的user’这样一个查询,那么通过以下的Lua脚本就可以实现public static final String SCRIPT =local resultKeys={};+ for k,v in ipairs(KEYS) do + local tmp = (hget, v, age);+ if tmp > ARGV[1] then + (resultKeys,v);+ end;+ end;+ return resultKeys;;执行脚本代码 Jedis jedis = ();(auth);List keys = (allUserKeys);List args = new ArrayList<>();(28);List resultKeys = (List)(funcKey, keys, args);return resultKeys;注意,以上的代码中使用的是evalsha命令,该命令参数的不是直接Lua脚本字符串,而是提前已经加载到Redis中的函数的一个SHA索引,通过以下的代码将系统中所有需要执行的函数提前加载到Redis中,我们的系统维护一个函数哈希表,后续需要实现什么功能,就从函数表中获取对应功能的SHA索引,通过evalsha调用就行。 String shaFuncKey = (SCRIPT);//加载脚本,获取sha索引(funcName_age, shaFuncKey);//添加到函数表中通过以上的方法,便可以使较为复杂的查询放到Redis中去执行,提高效率。

LED电子显示屏系统的分类

LED是发光二极管Light Emitting Diode 的英文缩写。 LED显示屏是由发光二极管排列组成的一显示器件。 它采用低电压扫描驱动,具有如下优点:1、耗电省、2、使用寿命长、3、成本低、4、亮度高、5、视角大、6、可视距离远、7、规格品种多。 LED显示产品系列:A、单色、彩色条形显示屏、B、计算机控制数码显示屏、C、单色图文显示屏、D、三色(红、绿、黄)图文显示屏、E、点阵和数码混合显示屏(证券屏)、F、双基色(红、绿)多媒体视频同步显示屏、G、三基色(红、绿、蓝)多媒体视频同步显示屏LED显示屏分类:按显示颜色分为:单红色、单绿色、红绿双基色、 红绿蓝三色按使用功能分为:图文显示屏、多媒体视频显示屏、行情显示屏、条形显示屏按使用环境分为:室内显示屏、室外显示屏、半户外显示屏按发光点直径分为:φ3.0、φ3.7、φ4.8、φ5.0、φ8.0、ph8、ph10、ph16、ph20等。 基本发光点非行情类LED显示屏中,室内LED显示屏按采用的LED单点直径可分为Φ3mm、Φ3.75mm、Φ5mm、Φ8mm、和Φ10mm等显示屏;室外LED显示屏按采用的象素直径可分为Φ16mm、Φ19mm、Φ22mm和Φ26mm等LED显示屏。 行情类LED显示屏中按采用的数码管尺寸可分2.0cm(0.8inch)、2.5cm(1.0inch)、3.0cm(1.2inch)、4.6cmm(1.8inch)、5.8cm(2.3inch)、7.6cm(3inch)等LED显示屏。

显示颜色:LED显示屏按显示颜色分为单基色LED显示屏(含伪彩色LED显示屏),双基色LED显示屏和全彩色(三基色)LED显示屏。 按灰度级又可分为16、32、64、128、256级灰度LED显示屏等。 显示性能:LED显示屏按显示性能分为文本LED显示屏、图文LED显示屏,计算机视频LED显示屏,电视视频LED显示屏和行情LED显示屏等。 行情LED显示屏一般包括证券、利率、期货等用途的LED显示屏。

本文版权声明本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请联系本站客服,一经查实,本站将立刻删除。

发表评论

热门推荐