当前位置: 首页 > 后端技术 > PHP

Redis分布式锁--PHP

时间:2023-03-29 18:27:27 PHP

Redis分布式锁功能在单机环境下,有闪购活动。在短时间内,服务器压力和流量会突然增加。这会产生并发问题。如果要解决并发,需要解决以下几个问题:1.提高系统吞吐率,即每秒qps处理的请求数。2、避免高并发情况下资源争用导致的超买超买问题。内存数据库通过提高系统的qps来解决第二个问题:需要使用经常遇到的锁。比如MySQL有读锁、写锁、排它锁、悲观锁、乐观锁。但是这里我们只讨论redis实现锁的简单版本。设置锁$redis=newRedis();$redis->connect('127.0.0.1',6379);//连接Redis$expire=10;//有效期为10秒$key='lock';//key$value=time()+$expire;//锁值=Unix时间戳+锁有效期$lock=$redis->setnx($key,$value);//判断是否加锁成功,如果成功则执行下一步if(!empty($lock)){//下一步...}如果能用这么一个简单版的锁解决所有问题,那就太小看锁在程序中的应用了。正常的操作例子基本都是这样写的。但是这种写法也存在一些问题。1、如果有10000个请求访问redis中不存在的key,这样的请求指的是接收MySQL数据,导致CPU短时间内达到100%甚至崩溃。这种场景通常被称为缓存崩溃导致的缓存雪崩。2.如果CPU太高或者网络延迟问题导致锁没有被删除或者缓存key过期没有被回收,就会出现死锁。解决问题:引用reidssetnx方法的作用是当设置的key不存在时,设置一个新的值。这样就避免了缓存崩溃的问题。检测key的过期时间避免死锁,解决死锁问题$expire=10;//有效期10秒$key='lock';//key$value=time()+$expire;//锁定值=Unix时间戳+锁有效期$status=true;while($status){$lock=$redis->setnx($key,$value);如果(空($lock)){$value=$redis->get($key);如果($valuedel($key);}}else{$status=false;//下一步....}}1。按理说这样解决单机版本锁竞争问题问题不大。那么一个特殊的情况来了,如果设置锁的key因为意外情况没有被删除,也会出现死锁的情况。2、在分布式集群业务业务场景下,各个服务器独立存在。多台服务器如何竞争同一个标识符的锁?这里使用的是分布式锁,这里简单介绍一下,通过MYSQL事务机制进行扩展。事务的四个特性是ACID,有四种隔离级别:未提交读、提交读、可重复读和序列化。这些特性只在单台服务器上生效。当涉及到分布式集群时,数据在不同的服务器上,仅次于事务就很难保持数据的一致性和隔离性,所以事务的作用意义不大。Redis也是如此。分布式锁的正确打开方式/***实现Redis分布式锁*/$key='demo';//缓存KEY更新信息$lockKey='lock:'.$key;//设置锁KEY$lockExpire=10;//设置锁的有效期为10秒//获取缓存信息$result=$redis->get($key);//判断缓存中是否有数据if(empty($result)){$status=TRUE;while($status){//设置锁值为当前时间戳+有效期$lockValue=time()+$lockExpire;/***创建锁*尝试创建一个以$lockKey为key的缓存,值为Currenttimestamp*由于setnx()函数只有在当前key没有缓存的情况下才会创建成功*,所以这个函数可以用来判断当前执行的操作是否已经被其他进程执行过*@var[type]*/$lock=$redis->setnx($lockKey,$lockValue);/***满足两个条件之一即可操作*1.上面这一步创建锁成功;*2.1)判断锁的值(时间戳)小于当前时间$redis->get()*2)同时给锁设置新值成功$redis->getset()*/if(!empty($lock)||($redis->get($lockKey)getSet($lockKey,$lockValue)expire($lockKey,$lockExpire);//*******************************//插入和更新缓存操作在这里...//*****************************//上面程序完成后删除锁//检查锁是否过期,不需要删除过期锁if($redis->ttl($lockKey))$redis->del($lockKey);$状态=假;}else{/***如果存在有效锁,则在此处做相应的处理*等待当前操作完成后再执行本次请求*直接返回*/sleep(2);//等待尝试执行之后的操作2秒}}}完结篇从知识面(mysql)、示例代码的优缺点介绍、应用场景等方面区别于其他博文~