redis 的setnx,資料庫讀取快取

2021-08-03 14:57:16 字數 2026 閱讀 3077

在 redis 裡,所謂 setnx,是「set if not exists」的縮寫,也就是只有不存在的時候才設定,可以利用它來實現鎖的效果,不過很多人沒有意識到 setnx 有陷阱!

比如說:某個查詢資料庫的介面,因為呼叫量比較大,所以加了快取,並設定快取過期後重新整理,問題是當併發量比較大的時候,如果沒有鎖機制,那麼快取過期的瞬間,大量併發請求會穿透快取直接查詢資料庫,造成雪崩效應,如果有鎖機制,那麼就可以控制只有乙個請求去更新快取,其它的請求視情況要麼等待,要麼使用過期的快取。

下面以目前 php 社群裡最流行的 phpredis 擴充套件為例,實現一段演示**:

<?php

$ok = $redis->setnx($key, $value);

if ($ok)

?>

快取過期時,通過 setnx  獲取鎖,如果成功了,那麼更新快取,然後刪除鎖。看上去邏輯非常簡單,可惜有問題:如果請求執行因為某些原因意外退出了,導致建立了鎖但是沒有刪除鎖,那麼這個鎖將一直存在,以至於以後快取再也得不到更新。於是乎我們需要給鎖加乙個過期時間以防不測:

<?php

$redis->multi();

$redis->setnx($key, $value);

$redis->expire($key, $ttl);

$redis->exec();

?>

因為 setnx 不具備設定過期時間的功能,所以我們需要借助 expire 來設定,同時我們需要把兩者用 multi/exec 包裹起來以確保請求的原子性,以免 setnx 成功了 expire 卻失敗了。 可惜還有問題:當多個請求到達時,雖然只有乙個請求的 setnx 可以成功,但是任何乙個請求的 expire 卻都可以成功,如此就意味著即便獲取不到鎖,也可以重新整理過期時間,如果請求比較密集的話,那麼過期時間會一直被重新整理,導致鎖一直有效。於是乎我們需要在保證原子性的同時,有條件的執行 expire,接著便有了如下 lua **:

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

沒想到實現乙個看起來很簡單的功能還要用到 lua 指令碼,著實有些麻煩。其實 redis 已經考慮到了大家的疾苦,從 2.6.12 起,set 涵蓋了 setex 的功能,並且 set 本身已經包含了設定過期時間的功能,也就是說,我們前面需要的功能只用 set 就可以實現。

<?php

$ok = $redis->set($key, $value, array('nx', 'ex' => $ttl));

if ($ok)

?>

如上**是完美的嗎?答案是還差一點!設想一下,如果乙個請求更新快取的時間比較長,甚至比鎖的有效期還要長,導致在快取更新過程中,鎖就失效了,此時另乙個請求會獲取鎖,但前乙個請求在快取更新完畢的時候,如果不加以判斷直接刪除鎖,就會出現誤刪除其它請求建立的鎖的情況,所以我們在建立鎖的時候需要引入乙個隨機值:

<?php

$ok = $redis->set($key, $random, array('nx', 'ex' => $ttl));

if ($ok)

}?>

補充:本文在刪除鎖的時候,實際上是有問題的,沒有考慮到 gc pause 之類的問題造成的影響,比如 a 請求在 del 之前卡住了,然後鎖過期了,這時候 b 請求又成功獲取到了鎖,此時 a 請求緩過來了,就會 del 掉 b 請求建立的鎖,此問題遠比想象的要複雜,具體解決方案參見本文最後關於鎖的若干個參考鏈結。

如此基本實現了單機鎖,假如要實現分布鎖,請參考:distributed locks with redis,不過分布式鎖需要注意的地方更多:how to do distributed locking,is redlock safe。此外,還有中文版:基於redis的分布式鎖到底安全嗎(上/下)。

Redis中setnx的使用

setnx是 set if not exists 的縮寫,只有不存在的時候才設定,可以利用它來實現鎖的效果。setnx key value 若給定的 key 已經存在,則 setnx 不做任何動作。set命令可用選項的基本語法 set key value ex seconds px millisec...

資料庫的讀現象

讀現象 是多個事務併發執行時,在讀取資料方面可能碰到的狀況。先了解它們有助於理解各隔離級別的含義。其中包括髒讀 不可重複讀和幻讀。資料庫帶來的併發問題包括 當第二個事務選擇其它事務正在更新的行時,會發生未確認的相關性問題。第二個事務正在讀取的資料還沒有確認並且可能由更新此行的事務所更改。舉例1 e....

資料庫讀現象

資料庫管理軟體的 讀現象 指的是當多個事務併發執行時,在讀取資料方面可能碰到的問題,包括又髒讀,不可重複讀和幻讀.ps 對於一些資料庫軟體會自帶相應的機制去解決髒讀,不可重複讀,幻讀等問題,因為這些自帶的機制,下述的一些實驗可能在某一資料庫管理軟體 的預設機制下並不成立,即我們並不能在所有資料庫管理...