分布式鎖 - redis實現(xiàn)方案

在普通的單機程序中,我們?yōu)榱吮苊赓Y源競爭,通常會使用synchronize、lock 等方式進行加鎖防止并發(fā)問題。不過在分布式系統(tǒng)中,請求是并發(fā)的在多臺機器上執(zhí)行,這時候就需要使用分布式鎖來防止資源競爭問題。

提到分布式鎖,我們最常見的方案就是基于redis實現(xiàn),本文將依次解釋

  • 如何用redis實現(xiàn)一個基本的分布式鎖
  • 基本方案中還有哪些問題?如何解決?

redis分布式鎖

我們知道redis由于其單線程的模式,可以保證各命令按順序、原子的執(zhí)行。

Redis的分布式鎖主要使用了setnx命令。

  • 加鎖。使用 setnx key value,當(dāng)key不存在時,設(shè)置成功表示獲取到鎖,否則認(rèn)為獲取鎖失敗。
  • 解鎖。使用del key,刪除緩存對應(yīng)的key表示成功釋放鎖。

一般來說,為了防止發(fā)生由于服務(wù)問題導(dǎo)致解鎖命令未執(zhí)行而造成鎖一致無法被釋放(死鎖)的情況。會在加鎖時同時使用expire key timeout命令設(shè)置一個默認(rèn)的超時時間。由于setnxexpire 是兩個命令,為了保證原子性,可以通過lua腳本的方式進行執(zhí)行。

上述方案已經(jīng)可以實現(xiàn)一個基本的分布式鎖,但是還是會有一些特殊的場景及問題需要我們?nèi)リP(guān)注并解決。

分布式鎖特殊場景

  • 鎖錯誤刪除

    舉一個特殊場景的例子。線程A獲取鎖L,然后設(shè)置超時時間10s。然后線程B在12s時成功獲取鎖,然后開始執(zhí)行到18s。然后線程A在15秒時執(zhí)行完,就會去解鎖(刪除緩存key)。此時就發(fā)生了錯誤解鎖的問題,即釋放了線程B持有的鎖。

錯誤釋放鎖

這種場景,可以通過每個線程設(shè)置value都是與之唯一對應(yīng)的value來解決(例如UUID)。然后在del的時候做一個value校驗來防止誤刪除。

  • 超時解鎖導(dǎo)致并發(fā)問題

    上圖中除了鎖誤刪的情況,還因為 鎖超時自動釋放 進而導(dǎo)致了線程A和線程B并發(fā)問題。針對這種情況,一般的解決方案是為線程創(chuàng)建一個守護進程,來進行不斷的續(xù)約操作,避免鎖超時釋放。

守護線程續(xù)約

注意守護進程也需要進行value校驗,防止錯誤的續(xù)約操作。當(dāng)主線程執(zhí)行完業(yè)務(wù)后,同時關(guān)閉掉守護線程即可。(如果主線程意外關(guān)閉了,守護線程也會自動關(guān)閉)

其他問題

除了上述問題,redis本身在主從切換、腦裂問題等極端情況導(dǎo)致的數(shù)據(jù)不一致問題,也會導(dǎo)致分布式鎖的錯誤。

參考資料

https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容