你們可能都知道,鎖的存在本質上是爲了解決共享資源互斥訪問的問題,爲了解決這個問題,在單機系統中(一個進程),不少開發語言都提供了鎖的特性,好比說java的synchoronized、lock等;在分佈式系統中(多個進程),則須要實現分佈式鎖,由於語言層面的鎖特性不足以解決問題。php
關於分佈式鎖的概念、特性以及實現方式,網上有不少相關的文章,感興趣的童鞋能夠自行搜索。html
簡單講,分佈式鎖也須要知足通常開發語言提供的鎖的一些基本特性:java
* 互斥性:多個線程(可能位於不一樣的進程上)訪問共享資源時,同時只能有一個線程訪問。redis
阻塞性:一個線程訪問共享資源時,其餘線程應該被阻塞執行。數據庫
如今常見的分佈式鎖的實現方案有:緩存
基於數據庫實現分佈式鎖架構
基於緩存(redis,memcached etc.)實現分佈式鎖分佈式
...ide
上面分享了一些關於分佈式鎖的理論知識,接下來從liquibase和flyway兩個library來解析它們實現分佈式鎖的區別。
memcached
有同窗可能知道,liquibase和flyway是數據庫表結構改變的管理工具,這類工具的目的是使對數據庫表結構的改變作到自動化,以防止人工對數據庫表結構的改動帶來的風險。兩個工具的基本原理都相似,便是對數據庫表結構的每一次改動維護成一條changeset(changeset能夠是建立一個表,也能夠是增長一個字段等),當應用程序啓動時,會依次執行維護的changeset,一旦changeset被執行過,就不會被再執行,具體如何使用能夠查看:
liquibase:https://www.liquibase.org/index.html
目前,這兩個工具在不少項目中都有應用。
以前在項目(微服務架構)中,遇到過一個liquibase的問題:一個service用liquibase管理數據庫change,有時候service在啓動階段忽然crash,再次啓動,一直啓動不起來,控制檯一直看到以下日誌:
INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock....
在另一個場景,有時候也發現過相似的問題,一個service有兩個instance,在第一個instance啓動階段,因爲未知緣由忽然crash,這時候第二個instance再也啓動不起來,控制檯一樣看到和上面同樣的日誌:
INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock....
而一樣的,有的service使用的flyway,卻沒有遇到過這樣的問題。這是爲何呢?
固然,在正常狀況下,第一個service啓動沒問題,另一個service就會成功啓動起來。
其實,上面這個場景是典型的一個分佈式鎖應用的場景:service的兩個instance須要互斥訪問數據庫以執行changeset,第一個instance執行過程當中,第二個instance須要阻塞等待;第一個instance執行完了,會自動釋放鎖,接着第二個instance繼續執行。
因爲這兩個library自己就是數據庫相關的工具,自然就要依賴數據庫,因此採用的分佈式鎖的實現方案就是基於數據庫實現的方案。一般,基於數據庫實現分佈式鎖有兩種方式(參考https://blog.csdn.net/dingjianmin/article/details/82763871):
1.基於數據庫表
2.基於數據庫排他鎖
兩個library分別採用了這兩種方式,Liquibase採用的是第一種-基於數據庫表,Flyway採用的是第二種-基於數據庫排他鎖。
Liquibase維護了一張databasechangeloglock表來實現分佈式鎖。
Flyway則利用的是數據庫的排他鎖,以下圖源碼所示。(參考http://dbabullet.com/index.php/2018/03/29/best-practices-using-flyway-for-database-migrations/)
採用第一種基於數據庫表的實現方式,一個關鍵的問題就是,如何防止一個線程解鎖失敗,致使鎖記錄一直在數據庫中,其餘線程沒法再得到到鎖?而這個問題也就是上面項目中遇到的liquibase的問題,一個service instance忽然crash致使解鎖失敗,其餘線程沒法再得到到鎖。
對於這個問題,liquibase官網只給出了一個workaround去清理髒鎖,沒有具體的計劃fix這個問題。
而因爲flyway採起的是第二種基於數據庫排他鎖的方式,則不會有這個問題。由於基於數據庫的排他鎖,若是service忽然crash,service跟數據庫的鏈接也就會斷掉,加在表上的排他鎖就會自動釋放,進而接下來其餘線程能夠繼續得到鎖。
最後,針對分佈式鎖各類方案的解釋,網上有不少寫得挺好的文章,下面列出一些僅供參考:
漫畫:什麼是分佈式鎖?
漫畫:如何用Zookeeper實現分佈式鎖?