通常使用的鎖分為樂觀鎖,悲觀鎖這兩種,簡單介紹下這兩種鎖,作為本文的背景知識,對這類知識已經有足夠了解的同學可以跳過這部分。
其實說白了,就是好比乙個健身房裡只有一台跑步機,在健身房門口有個排號機,每個進健身房的人都得先領乙個號碼才能進入,如果跑步機上有人,則在一邊做做熱身、喝喝水,如果跑步機上沒人,則確認跑步機上當前顯示的號碼(上乙個用過跑步機的人的號碼)是否比自己手持的小,如果小,則可以使用;否則,就意味著過號,而過號在現實中我們的都知道要麼走,要麼重排,就是不能插隊,在系統中也是一樣的,通常是返回錯誤。
然後,也同樣通俗的解釋下,還是那個健身房。這次在門口不需要排號機了,而是掛著把鑰匙(只有一把),想進去的人必須拿到這把鑰匙才行,拿到鑰匙的人可以進入,不管是熱身、喝水還是跑步都可以,直到他出來把鑰匙掛回牆上,下乙個才能去爭取,拿到的才可以再進去。聽著好像有點不人性化,所以悲觀鎖比較適合強一致性的場景,但效率比較低,特別是讀的併發低。樂觀鎖則適用於讀多寫少,併發衝突少的場景。
先說下,本文的開發背景,方便大家了解為什麼要使用悲觀鎖以及文中鎖的詳細設計。
任務分發系統:任務池(mysql)中存在大量任務(文章),現在需要使用者協助編輯,系統基本需求如下(簡化版):
1、推送使用者感興趣的分類下的任務到使用者編輯器中;
2、使用者編輯提交乙個任務後,自動推送下乙個任務;
3、每次只分配乙個任務給使用者;
4、如果乙個使用者占有某任務超過一定時間,則自動釋放任務,任務進任務池,重新迴圈;
5、……
目標有兩個:
1、乙個任務在同一時間段內只能被乙個使用者所持有;
2、避免出現死任務,即避免任務被使用者長時間占有,無法釋放。
由於系統併發量較大,並且有頻繁的寫操作,所以選擇悲觀鎖來控制每個任務只能同時被乙個使用者領取。主要思路如下:
1、從任務池中找出一部分可分配的任務;
2、根據一定順序,選擇乙個任務,作為候選推送任務;
3、嘗試對候選推送任務加鎖;
4、如果加鎖成功,則推送任務給使用者,並修改對應的任務狀態和使用者狀態;
5、如果加鎖失敗,則任務已被領取,重複2-5,直到推送成功。
這裡只介紹下鎖的實現機制,其餘業務邏輯略過。由於加鎖過程應該是不可拆解的,也就是常說的原子型操作,因此這裡選擇redis中的setnx操作作為加鎖的方法。
簡化版的**如下:
function lock($strmutex, $inttimeout)
return false;
}
這段**有個問題,就是setnx成功,但expire失敗,這就可能存在死任務的情況。解決這個問題的一種通用方法是通過使用incr方法代替setnx,具體如下:
function lock($strmutex, $inttimeout, $intmaxtimes = 0)
if ($intmaxtimes > 0 && $intret >= $intmaxtimes && $objredis->ttl($strmutex) === -1)
return false;
}
這段**通過$intmaxtimes來保證即使在expire未成功的時候也能強制解鎖,保證系統不會出現死任務。
還有沒有更好的方法呢?
其實redis中的set操作已相容了setnx,並且支援設定過期時間。
function lock($strmutex, $inttimeout)
return false;
}
這個方法是我認為目前最好的,但是為什麼沒有直接介紹這個方法,而是先介紹incr那個方法呢?其實細心的同學可以看到上面那個方面有兩個加粗的字」通用「。之所以這麼說是因為set方法是從redis2.6.12版本才開始支援多引數的。
參考資料:
用redis實現悲觀鎖(後端語言以php為例)
這裡只介紹下鎖的實現機制,其餘業務邏輯略過。由於加鎖過程應該是不可拆解的,也就是常說的原子型操作,因此這裡選擇redis中的setnx操作作為加鎖的方法。簡化版的 如下 這段 有個問題,就是setnx成功,但expire失敗,這就可能存在死任務的情況。解決這個問題的一種通用方法是通過使 functi...
Redis 事務(悲觀鎖 樂觀鎖)
1 定義 redis事務是乙個單獨的隔離操作 事務中所有的命令都會被序列化 按照順序執行 事務在執行過程中不會被其他客戶端傳送來的命令請求打斷 2 作用 串聯多個命令防止別的命令插隊 multi 輸入開始命令 exec 執行命令 discard 放棄組隊 刪除掉 3 注意事項 1 multi 命令不...
Redis鎖,悲觀鎖和樂觀鎖
樂觀鎖開啟事務前,設定對資料的監聽 watch exec時,如果發生資料發生過修改,作用於改資料的事務會自動取消 discard 事務exec後,無論成敗,監聽會被移除 悲觀鎖每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖。場景 如果專案中使用了快取且對快取設定了超時時間。當併發...