很多站长朋友们都不太清楚phpredis加锁,今天小编就来给大家整理phpredis加锁,希望对各位有所帮助,具体内容如下:
本文目录一览: 1、 redis会对数据加锁吗? 2、 redis分布式锁常见问题及解决方案 3、 Redlock算法-Redis官方分布式锁 4、 Redis怎么实现分布式锁 5、 如何使用redis实现分布式锁功能? 6、 php 怎么给redis加查询锁 redis会对数据加锁吗?亲。redis是没有锁机制的哟。对于多个用户连接也不存在竞争问题。
但是在进行并发时可能会出现连接超时,连接被阻塞或者是连接被关闭之类的错误。一般可以通过在客户端将连接做池化处理(比如使用synchronized,在读写redis时加内部锁),或者在服务器端用redis自带的事务处理命令setnx,来实现锁。
redis分布式锁常见问题及解决方案1.1 锁需要具备唯一性
1.2 锁需要有超时时间,防止死锁
1.3 锁的创建和设置锁超时时间需要具备原子性
1.4 锁的超时的续期问题
1.5 B的锁被A给释放了的问题
1.6 锁的可重入问题
1.7 集群下分布式锁的问题
问题讲解:
首先分布式锁要解决的问题就是分布式环境下同一资源被多个进程进行访问和操作的问题,既然是同一资源,那么肯定要考虑数据安全问题.其实和单进程下加锁解锁的原理是一样的,单进程下需要考虑多线程对同一变量进行访问和修改问题,为了保证同一变量不被多个线程同时访问,按照顺序对变量进行修改,就要在访问变量时进行加锁,这个加锁可以是重量级锁,也可以是基于cas的乐观锁.
解决方案:
使用redis命令setnx(set if not exist),即只能被一个客户端占坑,如果redis实例存在唯一键(key),如果再想在该键(key)上设置值,就会被拒绝.
问题讲解:
redis释放锁需要客户端的操作,如果此时客户端突然挂了,就没有释放锁的操作了,也意味着其他客户端想要重新加锁,却加不了的问题.
解决方案:
所以,为了避免客户端挂掉或者说是客户端不能正常释放锁的问题,需要在加锁的同时,给锁加上超时时间.
即,加锁和给锁加上超时时间的操作如下操作:
>setnx lockkey true #加锁操作
ok
>expire lockkey 5 #给锁加上超时时间
... do something critical ...
>del lockkey #释放锁
(integer) 1
问题讲解:
通过2.3加锁和超时时间的设置可以看到,setnx和expire需要两个命令来完成操作,也就是需要两次RTT操作,如果在setnx和expire两次命令之间,客户端突然挂掉,这时又无法释放锁,且又回到了死锁的问题.
解决方案:
使用set扩展命令
如下:
>set lockkey true ex 5 nx #加锁,过期时间5s
ok
... do something critical ...
>del lockkey
以上的set lockkey true ex 5 nx命令可以一次性完成setnx和expire两个操作,也就是解决了原子性问题.
问题讲解:
redis分布式锁过期,而业务逻辑没执行完的场景,不过,这里换一种思路想问题,把redis锁的过期时间再弄长点不就解决了吗?那还是有问题,我们可以在加锁的时候,手动调长redis锁的过期时间,可这个时间多长合适?业务逻辑的执行时间是不可控的,调的过长又会影响操作性能。
解决方案:
使用redis客户端redisson,redisson很好的解决了redis在分布式环境下的一些棘手问题,它的宗旨就是让使用者减少对Redis的关注,将更多精力用在处理业务逻辑上。redisson对分布式锁做了很好封装,只需调用API即可。RLock lock = redissonClient.getLock("stockLock");
redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗”
问题讲解:
A、B两个线程来尝试给key myLock加锁,A线程先拿到锁(假如锁3秒后过期),B线程就在等待尝试获取锁,到这一点毛病没有。那如果此时业务逻辑比较耗时,执行时间已经超过redis锁过期时间,这时A线程的锁自动释放(删除key),B线程检测到myLock这个key不存在,执行 SETNX命令也拿到了锁。但是,此时A线程执行完业务逻辑之后,还是会去释放锁(删除key),这就导致B线程的锁被A线程给释放了。
解决方案:
一般我们在每个线程加锁时要带上自己独有的value值来标识,只释放指定value的key,否则就会出现释放锁混乱的场景一般我们可以设置value为业务前缀_当前线程ID或者uuid,只有当前value相同的才可以释放锁
问题讲解:
上面我们讲了,为了保证锁具有唯一性,需要使用setnx,后来为了与超时时间一起设置,我们选用了set命令。 在我们想要在加锁期间,拥有锁的客户端想要再次获得锁,也就是锁重入
解决方案:
给锁设置hash结构的加锁次数,每次加锁就+1
问题讲解:
这一问题是在redis集群方案时会出现的.事实上,现在为了保证redis的高可用和访问性能,都会设置redis的主节点和从节点,主节点负责写操作,从节点负责读操作,也就意味着,我们所有的锁都要写在主redis服务器实例中,如果主redis服务器宕机,资源释放(在没有加持久化时候,如果加了持久化,这一问题会更加复杂),此时redis主节点的数据并没有复制到从服务器,此时,其他客户端就会趁机获取锁,而之前拥有锁的客户端可能还在对资源进行操作,此时又会出现多客户端对同一资源进行访问和操作的问题.
解决方案:
使用redlock,原理与zookeeper分布式锁原理相同.多台主机超过半数设置成功则获取锁成功,要注意下主机个数必须是奇数,不过这有效率问题
Redlock算法-Redis官方分布式锁在分布式版本的算法中,我们假设有N个Redis实例,这些节点是完全独立的,因此,我们不需要复制或任何其他的协调方法。在单机模式中,我们已经描述了怎样安全地取得和释放锁。我们认为Redlock算法在单机模式中将会使用这种方法去取得和释放锁是理所应当地。在我们地例子中N=5,这是一个合理地值,因此需要在5个不同的电脑或虚拟机上运行5个Redis实例以确保他们之间地相互独立。
为了取得锁,客户端执行以下操作:
1. 获取当前毫秒级时间Start。
2. 试着用相同的key和value顺序地对N个Redis实例进行加锁。该步骤中,在为每个实例设置锁的同时,客户端会使用一个超时时间timeout,timeout小于锁的自动释放时间ValidityTime。例如:如果ValidityTime=10s,则timeout应该在5~50ms之间。这可以使一个实例宕机时,客户端阻塞的时间更短,当一个Redis实例不可达时,应该尽快的去对另一个实例加锁。
3. 获取当前毫秒级时间End, End - Start = Elapsed, Elapsed是客户端为了获得锁而花费的时间。当且仅当客户端在所有实例中取得的锁的个数占多数,并且花费的总时间Elapsed小于锁的有效时间,此时,才会认为加锁成功。
4. 如果加锁成功,锁的有效时间则是ValidityTime = InitTime-Elapsed,Elapsed如步骤3中计算。
5. 如果客户端由于某些原因加锁失败,如ValidityTime<=0 或者 n < N/2 + 1。此时,客户端将会对这些实例进行unlock操作。
我们需要更好的说明这个排他锁的规则:只有当客户端在有效时间ValidityTime内完成工作才能保证该排他锁的有效性。
Redis怎么实现分布式锁阿粉最近迷上了 Redis,为什么呢?感觉 Redis 确实功能很强大呀,一个基于内存的系统 Key-Value 存储的数据库,竟然有这么多的功能,而阿粉也要实实在在地把 Redis 来弄一下,毕竟面试的时候,Redis 可以说是一个非常不错的加分项。
为什么需要分布式锁?
目前很多的大型项目全部都是基于分布式的,而分布式场景中的数据一致性问题一直是一个不可忽视的问题,大家知道关于分布式的 CAP 理论么?
CAP 理论就是说任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。
而我们的系统最终满足的永远都是最终一致性,而这种最终一致性,有些时候有人会喜欢问关于分布式事务,而有些人则偏重在分布式锁上。
但是阿粉选择的就是使用缓存来实现分布式锁,也就是我们在项目中最经常使用的 Redis ,谈到 Redis,那真是可以用在太多地方了,比如说:
我们今天就来实现用 Redis 来实现分布式锁,并且要学会怎么使用。
1.准备使用 Jedis 的 jar 包,在项目中导入 jar 包。
jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 这个加锁的姿势才是我们最需要了解的,不然你用的时候都不知道怎么使用。
key:加锁的键,实际上就是相当于一个唯一的标志位,不同的业务,你可以使用不同的标志位进行加锁。
requestId:这个东西实际上就是用来标识他是哪一个请求进行的加锁,因为在分布式锁中,我们要知道一件事,就是加锁的和解锁的,必须是同一个客户端才可以。
而且还有一种比较经典的就是 B 把 A 的锁给释放了,导致释放混乱,如果你不加相同的请求,A 线程处理业务,执行了加锁,锁的过期时间是5s, B线程尝试获取锁,如果 A 处理业务时间超过5s,这时候 A 就要开始释放锁,而B在这时候没有检测到这个锁,从而进行了加锁,这时候加锁的时候,A还没处理完对应业务,当他处理完了之后,再释放锁的话,要是就是直接把 B 刚加的锁释放了,要么就是压根都没办法释放锁。
SET_IF_NOT_EXIST:看字面意思,如果 key 不存在,我们进行Set操作,如果存在,啥都不干,也就不在进行加锁。
SET_WITH_EXPIRE_TIME:是否过期
expireTime:这是给 key 设置一个过期的时间,万一你这业务一直被锁着了,然后之后的业务想加锁,你直接给一直持有这个这个锁,不进行过期之后的释放,那岂不是要凉了。
上面的方法中 tryGetDistributedLock 这个方法也就是我们通常使用的加锁的方法。
大家看到这个 script 的时候,会感觉有点奇怪,实际上他就是一个 Lua 的脚本,而 Lua 脚本的意思也比较简单。
其实这时候就有些人说,直接 del 删除不行么?你试试你如果这么写的话,你们的领导会不会把你的腿给你打断。
这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,也就是说,这锁就算不是我加的,我都能开,这怎么能行呢?
在这里给大家放一段使用的代码,比较简单,但是可以直接用到你们的项目当中
我们先把这个实现方式实现了,然后我们再来说说大家最不愿意看的理论知识,毕竟这理论知识是你面试的时候经常会被问到的。
分布式CAP理论:
加州大学伯克利分校的 Eric Brewer 教授在 ACM PODC 会议上提出 CAP 猜想。2年后,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP。之后,CAP 理论正式成为分布式计算领域的公认定理。
也就是说,在二十年前的时候,CAP 理论只是个猜想。结果两年之后被证实了,于是,大家在考虑分布式的时候,就有根据来想了,不再是空想了。
什么是分布式的 CAP 理论 ?
一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项
这个和(Atomicity)不太一样,因为之前看有些人说,在 CAP 理论中的 A 和数据库事务中的 A 是一样的,单词都不一样,那能一样么?
Availability :分布式中的 A 表示的是可用性,也就是说服务一直可用,而且是正常响应时间。
而你在搭建分布式系统的时候,要保证每个节点都是稳定的,不然你的可用性就没有得到相对应的保证,也谈不上是什么分布式了。只能称之为一个伪分布式。
Consistency: 一致性
也就是说你的更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,这个如果你在使用 Redis 做数据展示的时候,很多面试官都会问你,那你们是怎么保证数据库和缓存的一致性的呢?
毕竟你只是读取的话,没什么问题,但是设计到更新的时候,不管是先写数据库,再删除缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。
所以如果你对这个很感兴趣,可以研究一下,比如说:
如果你能在面试的时候把这些都给面试官说清楚,至少感觉你应该能达到你自己的工资要求。
Partition tolerance:分区容错性
分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
其实在 CAP 理论当中,我们是没有办法同时满足一致性、可用性和分区容错性这三个特性,所以有所取舍就可以了。
关于使用 Redis 分布式锁,大家学会了么?
如何使用redis实现分布式锁功能?由于redis是单线程的且性能很快,所以比较适合做全局分布式锁。
基本流程就是在操作可能某个全局冲突资源的时候,使用一个全局唯一key来判断是否有其他线程占用了资源,如果有其他线程占用,则报错退出或者循环等待。如果没有其他线程占用,则就可以通过添加分布式锁来占用这个资源,然后再执行后续的任务,在任务执行完成之后,再释放分布式锁,其他线程就可以继续使用这个资源了。
那么通过redis加锁的动作是什么呢?
简单加锁命令:
命令是:setnx
内部的实现机制就是判断这个key位置是不是有数据,没有数据就设置成value返回,有数据就返回一个特殊数值。
但是这里有一个问题是,如果占用资源的线程错误退出了,没有来得及释放分布式锁,这个锁就被永远的占用了
改进版的加锁:
命令是:1. setnx 2. expire
添加分布式锁的同时,添加一个锁锁过期的时间。这样,当加锁线程退出之后,至少等一段时间之后,锁是有机会释放掉的。
这里有一个小问题是,这两个命令是分开执行的,不是原子操作。那么就存在理论上来说,第一个命令执行完之后,就出现错误,来不及执行expire命令的可能,一种办法是自己写lua脚本,可以实现多条命令的原子化执行。一种办法是引用一些开源库。在2.8版本之后,redis为了解决这个问题,提供了官方版的解法,就是命令:set key value nx expireTimeNum ex,将上述两个命令合并成了一个命令。
有了过期时间之后解决了一部分问题,但是也有可能出现锁都过期了,但是中间执行的任务还没有结束,第一个线程还在执行了,第二个线程已经拿到锁开始执行了,那么这时候第一个线程如果执行完成之后,那么就会将第二个线程的锁释放掉了。第二个线程释放锁的时候,要不然出错,要不然是释放的其他线程的锁,这样也会和预期不符。
如果单纯地要解决这个问题的话,可以在设置value的时候使用一个随机数,释放锁的时候,先判断这个随机数是否一致,如果一致再删除锁,否则就退出。但是判断value和删除key也不是一个原子操作,这时候就需要使用lua脚本了。
上面的方案依然不能解决超时释放的问题,依然违背分布式锁的初衷。怎么办了?
解题思路是另外启动一个线程,它的任务就是每隔一段时间判断一下如果发现当前线程的任务快过期了还没有完成,则定期给当前线程的锁续个期。
有个开源库解决了这个问题,它大概率会比你实现得更好一些。这个库就是redisson,非常好记,就是redis的儿子son,连起来就是reidsson,虽然可能不是亲的,但是也足够了。
这个库里面有一个组件是watchdog,直译过来就是看门狗,它的作用就是每隔一段时间判断的。
再继续思考,还有一个更极端的问题是,redis如果是单节点的,它宕机了;或者是主备节点的,但是备份节点还没有来得及同步主节点的数据,主节点拿到锁之后,在同步数据之前就马上宕机了,则也有可能出现锁不住的问题。如果认为这是一个问题,想要解决这个问题,这个问题怎么解决了?
思路是在加锁的时候多加锁几台redis服务器,通常情况下redis部署的时候是2n+1台,那么在加锁的时候需要保证过半数服务器加锁成功了,也就是说n+1台服务器。这时候除非整个集群都不可用了,则这个安全性将大幅度提升。
这个问题也有开源库解决了,就是redis红锁。
下一个问题是分布式锁可以重入么?
如果想要实现可重入的分布式锁的话,需要在设置value的时候加上线程信息和加锁次数的信息。但是这是简单的思路,如果加上过期时间等问题之后,可重入锁就可能比较复杂了。
php 怎么给redis加查询锁能不能加锁这个不知道,但是可以用监控watch 和事务结合起来用。因为watch的功能就是当它监控一个键的时候,如果这个键被修改了,那么它后面的事务就不会执行。
比如:
set key 1;
watch key
set key 2
mulit
set key 3
exec
get key =>'2' //key在watch后被修改了,所以后面的事务没有执行
关于phpredis加锁的介绍到此就结束了,不知道本篇文章是否对您有帮助呢?如果你还想了解更多此类信息,记得收藏关注本站,我们会不定期更新哦。
查看更多关于phpredis加锁 phpredis队列实现秒杀的详细内容...