天天看點

Redis 鎖機制

redis加鎖分類

INCR

SETNX

SET

加鎖應用場景

   鎖機制适用于多個使用者執行同一個操作,但不适用過多使用者,例如一些商城秒殺的促銷活動 

  防止重複請求

  如果使用者并發請求多次,而伺服器處理沒有加鎖限制,使用者則可以多次請求成功。例如換領優惠券,如果使用者同一時間并發送出換領碼,在沒有加鎖限制的情況下,使用者則可以使用同一個換領碼同時兌換到多張優惠券。

  防止并發請求

  例如某個查詢資料庫的接口因為請求量比較大是以加了緩存,并設定緩存過期後重新整理。當并發量比較大并且緩存過期的瞬間,大量并發請求會直接查詢資料庫導緻雪崩。如果使用鎖機制來控制隻有一個請求去更新緩存就能避免雪崩的問題。

INCR

這種加鎖的思路是, key 不存在,那麼 key 的值會先被初始化為 0 ,然後再執行 INCR 操作進行加一。然後其它使用者在執行 INCR 操作進行加一時,如果傳回的數大于 1 ,說明這個鎖正在被使用當中。

1、 用戶端A請求伺服器擷取key的值為1表示擷取了鎖
2、 用戶端B也去請求伺服器擷取key的值為2表示擷取鎖失敗
3、 用戶端A執行代碼完成,删除鎖
4、 用戶端B在等待一段時間後在去請求的時候擷取key的值為1表示擷取鎖成功
5、 用戶端B執行代碼完成,删除鎖
 
$redis->incr($key);
$redis->expire($key, $ttl); //設定生成時間為1秒      

SETNX

這種加鎖的思路是,如果 key 不存在,将 key 設定為 value,如果 key 已存在,則 

SETNX

 不做任何動作

1、 用戶端A請求伺服器設定key的值,如果設定成功就表示加鎖成功
2、 用戶端B也去請求伺服器設定key的值,如果傳回失敗,那麼就代表加鎖失敗
3、 用戶端A執行代碼完成,删除鎖
4、 用戶端B在等待一段時間後在去請求設定key的值,設定成功
5、 用戶端B執行代碼完成,删除鎖
    
$redis->setNX($key, $value);
$redis->expire($key, $ttl);      

SET

上面兩種方法都有一個問題,會發現,都需要設定 key 過期。那麼為什麼要設定key過期呢?如果請求執行因為某些原因意外退出了,導緻建立了鎖但是沒有删除鎖,那麼這個鎖将一直存在,以至于以後緩存再也得不到更新。于是乎我們需要給鎖加一個過期時間以防不測。但是借助 Expire 來設定就不是原子性操作了。是以還可以通過事務來確定原子性,但是還是有些問題,是以官方就引用了另外一個,使用 

SET

 指令本身已經從版本 2.6.12 開始包含了設定過期時間的功能。

1、 用戶端A請求伺服器設定key的值,如果設定成功就表示加鎖成功
2、 用戶端B也去請求伺服器設定key的值,如果傳回失敗,那麼就代表加鎖失敗
3、 用戶端A執行代碼完成,删除鎖
4、 用戶端B在等待一段時間後在去請求設定key的值,設定成功
5、 用戶端B執行代碼完成,删除鎖
        
$rs =$redis->set($key, $value, array('nx', 'ex' => $ttl));  //ex表示秒
if ($rs) {
    //處理更新緩存邏輯
    // ......
    //删除鎖
    $redis->del($key);
}      

到這一步其實還是有問題的,如果一個請求更新緩存的時間比鎖的有效期還要長,導緻在緩存更新過程中鎖就失效了,此時另一個請求就會擷取到鎖,但前一個請求在緩存更新完畢的時候,直接删除鎖的話就會出現誤删其它請求建立的鎖的情況。是以要避免這種問題,可以在建立鎖的時候需要引入一個随機值并在删除鎖的時候加以判斷。

$random=mt_rand();
$rs = $redis->set($key, $random, array('nx', 'ex' => $ttl));
if ($rs) {
     //處理更新緩存邏輯
    // ......
    //先判斷随機數,是同一個則删除鎖
    if ($redis->get($key) == $random) {
        $redis->del($key);
    }
}      

相關文章:正确地使用Redis的SETNX實作鎖機制

  redis加鎖的幾種實作

上一篇: 展望未來
下一篇: 類檔案結構

繼續閱讀