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

PHP实现Redis分布式锁

时间:2023-03-29 13:48:34 PHP

介绍在多线程的情况下,对一些共享资源的访问需要加锁,否则会导致数据混乱。分布式锁可以通过DB、Redis、Zk等方式实现,本节主要介绍PHP如何使用Redis实现分布式锁set命令setnxkeyvalue设置一个值。当key已经存在时,返回flase,表示失败。使用setnx实现分布式锁是有缺陷的。setnx操作无法设置密钥的ttl。需要和expriekeyttl一起使用。好在set命令集成了nx和ex操作setkeynameNXPX10000$redis=newRedis();$redis->connect('127.0.0.1',6380);$rs=$redis->set('testnx',123,['nx','ex'=>10]);var_dump($rs);//返回true表示加锁成功,返回false表示加锁失败。Redlockset命令还有另一个问题。当你想提前释放锁时,使用expire键0或者使用del键。如果给block发送expire或者del命令,锁会自动失效。这时,B获得了锁,expire/del命令到来,导致B获得的锁失效。Redlock在加锁的时候必须保证值的唯一性。释放锁时,验证该值与申请锁时的值是否一致classRedLock{private$retryDelay;私人$retryCount;私人$clockDriftFactor=0.01;私人$法定人数;私有$servers=array();私有$instances=array();函数__construct(array$servers,$retryDelay=200,$retryCount=3){$this->servers=$servers;$this->retryDelay=$retryDelay;$this->retryCount=$retryCount;$this->quorum=min(count($servers),(count($servers)/2+1));}publicfunctionlock($resource,$ttl){$this->initInstances();$token=uniqid();$retry=$this->retryCount;做{$n=0;$startTime=microtime(true)*1000;foreach($this->instancesas$instance){if($this->lockInstance($instance,$resource,$token,$ttl)){$n++;}}}#将2毫秒添加到漂移以考虑Redis过期#精度,即1毫秒,加上1毫秒最小漂移#对于小TTL。$drift=($ttl*$this->clockDriftFactor)+2;$validityTime=$ttl-(microtime(true)*1000-$startTime)-$drift;如果($n>=$this->quorum&&$validityTime>0){return['validity'=>$validityTi我,'resource'=>$resource,'token'=>$token,];}else{foreach($this->instancesas$instance){$this->unlockInstance($instance,$resource,$token);}}//在重试之前等待一个随机延迟$delay=mt_rand(floor($this->retryDelay/2),$this->retryDelay);usleep($延迟*1000);$重试--;}while($retry>0);返回假;}publicfunctionunlock(array$lock){$this->initInstances();$resource=$lock['resource'];$token=$lock['token'];foreach($this->instancesas$instance){$this->unlockInstance($instance,$resource,$token);}}}privatefunctioninitInstances(){if(empty($this->instances)){foreach($this->serversas$server){list($host,$port,$timeout)=$server;$redis=new\Redis();$redis->connect($host,$port,$timeout);$this->实例[]=$redis;}}}私有函数lockInstance($instance,$resource,$token,$ttl){return$instance->set($resource,$token,['NX','PX'=>$ttl]);}privatefunctionunlockInstance($instance,$resource,$token){$script='ifredis.call("GET",KEYS[1])==ARGV[1]thenreturnredis.call("DEL",KEYS[1])否则返回0结束';返回$instance->eval($script,[$resource,$token],1);}}$servers=[['127.0.0.1',6379,0.01],];$redLock=newRedLock($servers);while(true){$lock=$redLock->lock('test',10000);如果($lock){print_r($lock);$redLock->unlock(['resource'=>'test','token'=>'5d1c123121538']);}else{print"未获取锁\n";}}后续的Redis分布式锁有没有问题?解决方法:引入版本号参考:https://time.geekbang.org/col...

最新推荐
猜你喜欢