Redis 實現分佈式鎖

1.分佈式下的情況

分佈式與單機情況下最大的不同在於其不是多線程而是多進程
多線程由於可以共享堆內存,因此可以簡單的採取內存作爲標記存儲位置。而進程之間甚至可能都不在同一臺物理機上,因此需要將標記存儲在一個所有進程都能看到的地方。

2.分佈式鎖

當在分佈式模型下,數據只有一份(或有限制),此時需要利用鎖的技術控制某一時刻修改數據的進程數。
與單機模式下的鎖不僅需要保證進程可見,還需要考慮進程與鎖之間的網絡問題
分佈式鎖還是可以將標記存在內存,只是該內存不是某個進程分配的內存而是公共內存如 Redis、Memcache。至於利用數據庫、文件等做鎖與單機的實現是一樣的,只要保證標記能互斥就行。

3.Redis 實現分佈式鎖的思想

分佈式與單機的區別,無非在於鎖大概率不是在一臺機器上加的,或者說,多臺服務器開啓了多個服務,根本無法確定同時進行操作的兩個線程是在一個服務上。那麼對於鎖而言,單機情況是同進程下多線程,等於說鎖是對所有線程可見的,那麼在分佈式下,如果將鎖放到整個分佈式系統都可見的地方,自然能夠完成鎖的操作。只是不會是說利用 Java 語言的鎖,例如 synchronized 關鍵字等。
這個解決方案有很多**,Redis 是其中一種。即將鎖的數據放在 Redis 中。等於說,獲取鎖就是設置一個 key,然後放到 Redis 上,而線程進行操作的時候先去 Redis 上查看是否有這個 key。完成了整個鎖的思想。**

等於說對於鎖,還是有搶的過程:使用 setnx,若返回 1 說明搶鎖成功,同時完成了加鎖操作,根本沒有這個值,否則就是失敗。反之,使用 del 刪除 key,就是解鎖

4.原子性的問題

通過setnx加鎖會非常容易引起死鎖,因爲如果線程在執行過程中掛掉了,沒有釋放鎖,那麼就會造成死鎖。這個解決方法非常簡單,考慮在加鎖後,使用 expire 設定超時時間,超時自動釋放。

上述的方法有一個比較重要的問題,就是執行了兩個命令,等於說是非原子性的操作。這樣有可能引起非常致命的錯誤,所以解決方法上需要考慮用 set 命令代替。

set key value EX time

5.誤刪鎖的問題

雖然設置了超時時間,但是超時時間的設定卻有待商榷。因爲無法確定這個方法會不會出現問題,假設方法出現了問題,設定的超時時間是 20s,方法來個超級大的慢查詢,20s 還沒有執行成功。此時 key 自動失效,等於說方法沒有執行成功卻釋放了鎖

此時其他線程的請求會獲得鎖,然後執行方法。如果恰好該線程也出問題了,而之前的方法執行完畢,執行了 del 等於說提前將別的鎖給解了。

解決方法就是確定,鎖只有當前線程才能解,那麼就是在設置 key 的時候,value 還什麼都沒設置呢,將線程 id 設置進去就可以了。

而這樣解決了誤刪鎖的問題,還有自動釋放鎖的問題。或者說是超時時間的設定問題。一個解決方法是,設定一個時間,時間無所謂,但是給線程開啓一個守護進程。當守護進程發現時間快到了,不管方法執行情況,直接續時。這樣可能會消費很長的時間,但是保證線程必然會保持鎖直到完成。如果碰見掛掉的情況,此時守護進程會跟隨掛掉,所以自然不能續時,保證了不會死鎖。
也可以看到,上述條件可能會引起飢餓,但是這個就不是分佈式鎖的問題了,而是程序的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章