爲什麼用分佈式鎖?
在討論這個問題之前,我們先來看一個業務場景:
系統A是一個電商系統,目前是一臺機器部署,系統中有一個用戶下訂單的接口,但是用戶下訂單之前一定要去檢查一下庫存,確保庫存足夠了纔會給用戶下單。
由於系統有一定的併發,所以會預先將商品的庫存保存在redis中,用戶下單的時候會更新redis的庫存。
此時系統架構如下:
但是這樣一來會產生一個問題:
假如某個時刻,redis裏面的某個商品庫存爲1,此時兩個請求同時到來,其中一個請求執行到上圖的第3步,更新數據庫的庫存爲0,但是第4步還沒有執行。
而另外一個請求執行到了第2步,發現庫存還是1,就繼續執行第3步。
這樣的結果,是導致賣出了2個商品,然而其實庫存只有1個。
很明顯不對啊!這就是典型的庫存超賣問題 !!!
此時,我們很容易想到解決方案:
用鎖把2、3、4步鎖住,讓他們執行完之後,另一個線程才能進來執行第2步。
按照上面的圖,在執行第2步時,使用Java提供的synchronized或者ReentrantLock來鎖住,然後在第4步執行完之後才釋放鎖。
這樣一來,2、3、4 這3個步驟就被“鎖”住了,多個線程之間只能串行化執行。
但是好景不長,整個系統的併發飆升,一臺機器扛不住了。
現在要增加一臺機器,如下圖:
增加機器之後,系統變成上圖所示,我的天!
假設此時兩個用戶的請求同時到來,但是落在了不同的機器上,那麼這兩個請求是可以同時執行了,還是會出現庫存超賣的問題。
爲什麼呢?因爲上圖中的兩個A系統,運行在兩個不同的JVM裏面,他們加的鎖只對屬於自己JVM裏面的線程有效,對於其他JVM的線程是無效的。
因此,這裏的問題是:Java提供的原生鎖機制在多機部署場景下失效了。
這是因爲兩臺機器加的鎖不是同一個鎖(兩個鎖在不同的JVM裏面)。
那麼,我們只要保證兩臺機器加的鎖是同一個鎖,問題不就解決了嗎?
此時,就該分佈式鎖隆重登場了,分佈式鎖的思路是:
在整個系統提供一個全局、唯一的獲取鎖的“東西”,然後每個系統在需要加鎖時,都去問這個“東西”拿到一把鎖,這樣不同的系統拿到的就可以認爲是同一把鎖。
至於這個“東西”,可以是 Redis、Zookeeper,也可以是數據庫。
文字描述不太直觀,我們來看下圖:
通過上面的分析,我們知道了庫存超賣場景在分佈式部署系統的情況下使用Java原生的鎖機制無法保證線程安全,所以我們需要用到分佈式鎖的方案。
那麼,如何實現分佈式鎖呢?接着往下看!
待更新....