系列文章redis
當多個進程不在同一個系統中,就須要用分佈式鎖控制多個進程對資源的訪問。bash
使用redis來實現分佈式鎖主要用到如下命令:微信
SETNX KEY VALUE
若是key不存在,就設置key對應字符串value併發
expire KEY seconds
設置key的過時時間dom
del KEY
刪除key異步
代碼實現以下:分佈式
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$ok = $redis->setNX($key, $value);
if ($ok) {
//獲取到鎖
... do something ...
$redis->del($key);
}
複製代碼
上面代碼有沒有問題呢? 若是咱們在邏輯處理過程當中出現了異常狀況,致使KEY沒有刪除,那就出現了死鎖了。因此通常咱們在拿到鎖以後再給KEY加一個過時時間post
爲了保證執行的原子性,使用了multi
就有了以下代碼ui
$redis->multi();
$redis->setNX($key, $value);
$redis->expire($key, $ttl);
$res = $redis->exec();
if($res[0]) {
//獲取到鎖
... do something ...
$redis->del($key);
}
複製代碼
可是這樣的又有一個問題第一個請求成功了,以後的請求雖然沒有拿到鎖可是每次都刷新了鎖的時間。這樣咱們設置鎖過時時間的意義就不存在了。因此咱們在拿到鎖之後再進行過時時間的操做,這時候咱們就能夠祭出原子性操做的lua腳本,代碼以下lua
$script = <<<EOT
local key = KEYS[1]
local value = KEYS[2]
local ttl = KEYS[3]
local ok = redis.call('setnx', key, value)
if ok == 1 then
redis.call('expire', key, ttl)
end
return ok
EOT;
$res = $redis->eval($script, [$key,$val, $ttl], 3);
if($res) {
//獲取到鎖
... do something ...
$redis->del($key);
}
複製代碼
藉助lua腳本雖然解決了問題,可是未免有些麻煩,Redis從 2.6.12 版本開始, SET 命令的行爲能夠經過一系列參數來修改:
$ok = $redis->set($key, $random, array('nx', 'ex' => $ttl));
if ($ok) {
//獲取到鎖
... do something ...
if ($redis->get($key) == $random) {
$redis->del($key);
}
}
複製代碼
能夠看到上面咱們咱們的值引入了一個隨機數,這是爲了防止邏輯處理時間過長致使鎖的過時時間已經失效,這時候下一個請求就得到了鎖,可是前一個請求在邏輯處理完直接刪除了鎖。
鎖主要用在併發請求如秒殺等場景中,以上即是redis鎖的實現。
本文亦在微信公衆號【小道資訊】發佈,歡迎掃碼關注!