php結合redis高並發下,悲觀鎖解決數據二次寫入


在做數據緩存的時候,通常都是把數據從數據庫讀取出來,然后放入緩存,接下來在緩存的有效期內都是從緩存讀取數據減少數據庫壓力。但是在高並發環境下,就有可能出現問題,比如根據指定格式從redis下拿數據,但是當下key是不存在的,那么就需要往里面寫數據,如果多個進程同時請求,會造成數據的二次寫入,如果邏輯不復雜還不會出現大的問題,問題是假如這個key的數據會變化呢?那么這時候就需要加一個鎖機制了,就是獲取了鎖權限的進程才有資格對數據操作。

提到悲觀鎖,先通過網上給出的一個比較形象的比喻

拿健身房比喻,門口掛着把鑰匙(只有一把),想進去的人必須拿到這把鑰匙才行,拿到鑰匙的人可以進入,不管是熱身、喝水還是跑步都可以,直到他出來把鑰匙掛回牆上,下一個才能去爭取,拿到的才可以再進去。

聽着好像有點不人性化,所以悲觀鎖比較適合強一致性的場景,但效率比較低,特別是讀的並發低。樂觀鎖則適用於讀多寫少,並發沖突少的場景。

實現要點和思路

1、一個任務在同一時間段內只能被一個用戶所持有; 
2、避免出現死任務,即避免任務被用戶長時間占有,無法釋放。

設置一個鎖的key,setnx是原子操作,只能一個進程寫入成功,寫入成功返回true(表示獲取鎖權限),然后寫入內容立即釋放鎖即刪除鎖key。如果只用SETNX命令設置鎖的話,如果當持有鎖的進程崩潰或刪除鎖失敗時,其他進程將無法獲取到鎖,問題就大了。獲取不到鎖的進程去判斷鎖的剩余有效時間,如果為-1,那么表示沒有設置過期時間,則設置鎖的有效時間為5秒(預留5秒給拿到鎖的進程處理時間,足夠多了),返回true,等待鎖刪除。

<?php $lock_key = 'LOCK_PREFIX' . $redis_key; $is_lock = $redis->setnx($lock_key, 1); // 加鎖
if($is_lock == true){ // 獲取鎖權限
    $redis->setex($redis_key, $expire, $data); // 寫入內容 // 釋放鎖
    $redis->del($lock_key); }else{ // 防止死鎖
    if($redis->ttl($lock_key) == -1){ $redis->expire($lock_key, 5); } return true; // 獲取不到鎖權限,直接返回
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM