分布式鎖,再深一點!!
一、起因
分布式環境下,多臺機器上多個進程對一個數據進行操作,必然引起數據不一致的情況,比如“商品超賣”。那么在分布式環境下,怎么訪問臨界資源,是互聯網的一大難題。分布式鎖就是一種解決方法。
二、互斥原理
原理:多個訪問方對同一個資源進行操作,需要進行互斥,通常是利用一個這些訪問方同時能夠訪問到的lock來實施互斥的。
場景一
在同一個進程內,多個線程的互斥,我們可以通過加鎖來進行串行化訪問。
步驟:
- 多個線程同時搶鎖
- 只一個線程搶到,未搶到的阻塞,或下次再來搶
- 搶到鎖的線程操作臨界資源
- 操作完臨界資源后釋放鎖
畫外音:鎖是進程內的一個數據結構,將臨界資源的沖突轉變為對鎖結構的沖突。
場景二
在分布式環境下,進程內的鎖結構就無法作用于進程外了,所以多進程情況下怎么進行臨界資源的保護呢?
結合進程內鎖的機制,我們可以得出幾點條件:
- 需要有一個特殊的數據結構,每個進程都能訪問
- 同時只能一個進程訪問成功
- 訪問成功的進程可以訪問臨界資源
畫外音:問題的關鍵在于找到同時只有一個進程訪問成功的外部存儲結構。
三、分布式鎖
既然分布式鎖的核心是選擇合適的外部存儲,那怎么選擇“合適”的存儲介質和存儲模型就是我們思考的核心了。
那分布式鎖用關系型存儲還是KV存儲?
從鎖的角度來看,我們對它要求不多,KV存儲足夠。這樣我們第一想到的就是Redis方案了,那是不是Redis方案就是最優方案呢?
Redis:單線程高性能的內存KV存儲方案。
- 滿足所有進程都能訪問的數據結構
- 單線程滿足只有一個進程能訪問成功(setnx命令)
- 業務上保證set成功的進程進行臨界資源操作
步驟:
- 多臺機器上多個進程對這個鎖進行爭搶,例如在緩存上同時進行set key=123操作
- 只有一個進程會搶到這個鎖,即只有一個進程對緩存set key=123能夠成功,不成功的進程下次再來搶
- 搶到鎖的進程對臨界資源進行操作
- 扣減完成之后釋放鎖,即對緩存delete key=123
從功能上,Redis完美的實現了分布式鎖,那再深入一點。
- MySQL能不能實現分布式鎖?
- ZooKeeper實現分布式鎖和Redis實現的本質區別是什么?
- 不同的架構選型,我們應該怎么選擇?