php+redis+lua分散ロックを実現
7053 ワード
以下は私が仕事で使っているクラス、redisロックの2つの方法で、ロックを解除するのは原子性を保証するためにlua+redisだけの方法です.
欠陥:デッドロックの問題は解決しましたが、ビジネスの実行時間がロックの有効期間を超えても、マルチクライアントのロックの問題があります.しかし、このクラスは私の現在のビジネスニーズを満たしています.
より優れたソリューションについては、次の2つの記事を参照してください.https://redis.io/topics/distlock(Redlockのアルゴリズム記述)https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA
コード実装:
欠陥:デッドロックの問題は解決しましたが、ビジネスの実行時間がロックの有効期間を超えても、マルチクライアントのロックの問題があります.しかし、このクラスは私の現在のビジネスニーズを満たしています.
より優れたソリューションについては、次の2つの記事を参照してください.https://redis.io/topics/distlock(Redlockのアルゴリズム記述)https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA
コード実装:
class RedisLock
{
/**
* @var ,
*/
private $_lockFlag;
private $_redis;
public function __construct($host = '127.0.0.1', $port = '6379', $passwd = '')
{
$this->_redis = new Redis();
$this->_redis->connect($host, $port);
if ($passwd) {
$this->_redis->auth($passwd);
}
}
public function lock($key, $expire = 5)
{
$now= time();
$expireTime = $expire + $now;
if ($this->_redis->setnx($key, $expireTime)) {
$this->_lockFlag = $expireTime;
return true;
}
//
$currentLockTime = $this->_redis->get($key);
if ($currentLockTime < $now) {
/*
C0 , , C1/C2/...
C1/C2 getset ( getset ,
C1/C2 ) */
$oldLockTime = $this->_redis->getset($key, $expireTime);
if ($currentLockTime == $oldLockTime) {
$this->_lockFlag = $expireTime;
return true;
}
}
return false;
}
public function lockByLua($key, $expire = 5)
{
$script = <<
$this->_lockFlag = md5(microtime(true));
return $this->_eval($script, [$key, $this->_lockFlag, $expire]);
}
public function unlock($key)
{
$script = <<
if ($this->_lockFlag) {
return $this->_eval($script, [$key, $this->_lockFlag]);
}
}
private function _eval($script, array $params, $keyNum = 1)
{
$hash = $this->_redis->script('load', $script);
return $this->_redis->evalSha($hash, $params, $keyNum);
}
}
$redisLock = new RedisLock();
$key = 'lock';
if ($redisLock->lockByLua($key)) {
// to do...
$redisLock->unlock($key);
}