色婷婷狠狠18禁久久YY,CHINESE性内射高清国产,国产女人18毛片水真多1,国产AV在线观看

nx函數用法

老白2年前32瀏覽0評論

nx函數用法?

在 Redis 里,所謂 SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設置,可以利用它來實現鎖的效果,不過很多人沒有意識到 SETNX 有陷阱!

比如說:某個查詢數據庫的接口,因為調用量比較大,所以加了緩存,并設定緩存過期后刷新,問題是當并發量比較大的時候,如果沒有鎖機制,那么緩存過期的瞬間,大量并發請求會穿透緩存直接查詢數據庫,造成雪崩效應,如果有鎖機制,那么就可以控制只有一個請求去更新緩存,其它的請求視情況要么等待,要么使用過期的緩存。

下面以目前 PHP 社區里最流行的 PHPRedis 擴展為例,實現一段演示代碼:

<?php

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

if ($ok) {

$cache->update();

$redis->del($key);

}

?>

緩存過期時,通過 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) {

$cache->update();

$redis->del($key);

}

?>

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

<?php

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

if ($ok) {

$cache->update();

if ($redis->get($key) == $random) {

$redis->del($key);

}

}

?>