推薦閱讀

1. Java 性能優化:教你提高代碼運行的效率

2. Java問題排查工具清單

3. 記住:永遠不要在MySQL中使用UTF-8

4. Springboot啟動原理解析

在分散式集群系統的開發中,線程鎖往往並不能支持全部場景的使用,必須引入新的技術方案分散式鎖。

線程鎖,進程鎖,分散式鎖

線程鎖:大家都不陌生,主要用來給方法、代碼塊加鎖。當某個方法或者代碼塊使用鎖時,那麼在同一時刻至多僅有有一個線程在執行該段代碼。當有多個線程訪問同一對象的加鎖方法/代碼塊時,同一時間只有一個線程在執行,其餘線程必須要等待當前線程執行完之後才能執行該代碼段。但是,其餘線程是可以訪問該對象中的非加鎖代碼塊的。

進程鎖:也是為了控制同一操作系統中多個進程訪問一個共享資源,只是因為程序的獨立性,各個進程是無法控制其他進程對資源的訪問的,但是可以使用本地系統的信號量控制(操作系統基本知識)。

分散式鎖:當多個進程不在同一個系統之中時,使用分散式鎖控制多個進程對資源的訪問。

分散式鎖到底是什麼,怎麼實現?

intsmaze說簡單點,實現分散式鎖必須要依靠第三方存儲介質來存儲鎖的元數據等信息。比如分散式集群要操作某一行數據時,這個數據的流水號是唯一的,那麼我們就把這個流水號作為一把鎖的id,當某進程要操作該數據時,先去第三方存儲介質中看該鎖id是否存在,如果不存在,則將該鎖id寫入,然後執對該數據的操作;當其他進程要訪問這個數據時,會先到第三方存儲介質中查看有沒有這個數據的鎖id,有的話就認為這行數據目前已經有其他進程在使用了,就會不斷地輪詢第三方存儲介質看其他進程是否釋放掉該鎖;當進程操作完該數據後,該進程就到第三方存儲介質中把該鎖id刪除掉,這樣其他輪詢的進程就能得到對該鎖的控制。

Redis中當然不能通過get,set操作判斷,get,set操作不是一個原子的,可以使用redis的jedis.set(String key, String value, String nxxx, String expx, int time)命令來保證原子性。

具體實現方案:

cnblogs.com/linjiqin/p/

說了這麼多,再補充一點,線程鎖,進程鎖,分散式鎖的作用都是一樣的,只是作用的範圍大小不同。範圍大小:分散式鎖——大於——進程鎖——大於——線程鎖。能用線程鎖,進程鎖情況下使用分散式鎖也是可以的,能用線程鎖的情況下使用進程鎖也是可以的。只是範圍越大技術複雜度就越大。

多年j2EE開發生涯從未感覺到分散式鎖的痛點!!!

關於分散式鎖,有過javaEE開發經驗的就會說了,系統為了應對高並發,會搭建一個比如tomcat集群,集群內服務都是訪問的同一台資料庫,有多台伺服器同時修改同一條資料庫數據的操作,但是我們並沒有在伺服器中使用分散式鎖?按照上面對分散式鎖的解釋,兩個不同系統上的JVM進程同時訪問資料庫的同一個資源,這個時候我們應該使用分散式鎖進行控制。

這說的沒有錯,但是我們忘記了資料庫的特性了。如果兩台伺服器僅僅是直接訪問(通過url)並操作某台伺服器硬碟中某個文件同一行數據,這個時候我們必須用分散式鎖。

但是因為這兩台伺服器訪問的數據是存儲在資料庫中的(資料庫本身就是一個服務程序,多線程的接收外部系統發來的請求),兩台伺服器的請求通過網路IO發送到資料庫伺服器後,然後把請求交給資料庫服務的進程處理,資料庫伺服器是多線程接收請求並處理的,這個時候關於某表某一行數據的多線程訪問控制是由資料庫服務進行控制的(就是資料庫服務的代碼中進行了線程上的加鎖處理),這就是資料庫伺服器的行鎖等特性,因為資料庫那一端已經對外部多個系統的請求進行了一個鎖操作,所以不需要我們在應用服務端進行分散式鎖的開發。 

那如果想同時更新資料庫的多行數據,這個時候資料庫的行鎖就無法保證了。這個時候我們就要使用分散式鎖,是的這個時候就可以使用,注意我用的是可以。為什麼說可以呢?因為資料庫本身就提供了這個機制,事務以及他的隔離級別。當然你也可以不用資料庫提供的事務,用分散式鎖。

分散式鎖的設計不需要考慮業務嗎?

分散式鎖的設計並不是完全美好的,只能針對某些業務場景下使用,如果要對所有業務使用,必須充分理解業務需求合理的設計,至於原因就和各位j2ee開發時mybatis的二級緩存以命名空間為單位所要注意的業務問題時一樣的。  

intsmaze使用分散式鎖,我們會把某表的第二第三行作為id來鎖住,如果有相同的操作時更新該表第二第三行,我們才不讓他修改,必須讓他拿到鎖才可以。但是如果有個操作僅僅是修改第二行,這個時候他就獲得了對該行的操作,而且等資料庫釋放掉之前操作對該行的鎖後。所以分散式鎖並不是隨處可用的,只是在某些場景下可以使用。比如業務系統不會存在單獨修改第二行的操作。

分散式鎖用於hbase存儲系統

實際開發場景中,我們會對hbase操作進行分散式鎖,hbase作為一款優秀的非內存資料庫,傳統資料庫一樣提供了事務的概念,只是HBase的事務是行級事務,可以保證行級數據的原子性、一致性、隔離性以及持久性,即通常所說的ACID特性。為了實現事務特性,HBase採用了各種並發控制策略,包括各種鎖機制、MVCC機制等。因為hbase只支持行級事物,當業務需要並發操作兩行甚至多行記錄時,hbase本身就無法提供ACID的支持了。

資料庫訪問量過大除了主從還能如何負載壓力?

資料庫會為客戶端的每一個請求創建一個線程,這些線程針對特定行數據修改必須獲得該行的行鎖,而其他客戶端線程要想修改該數據的話,必須等待前面的線程釋放鎖後才被允許。如果客戶端很多線程都要修改某行數據的話,沒有拿到鎖的線程都會在資料庫端機器上不斷輪詢,增大資料庫端的壓力。

我們可以使用分散式鎖,把對資料庫行鎖的等待獲取的輪詢放到每一個客戶端機器上去實現,這樣可以避免資料庫端線程的不斷輪詢。比如,客戶端在要發送對資料庫的某行數據的操作請求前,在客戶端機器上進行鎖的爭搶,沒有獲取到鎖,就不會像資料庫端發送操作請求,這樣資料庫端就沒有了輪詢的壓力。當然分散式鎖的引入一定要結合業務的需求來進行設計,不然會出現鎖id的命名不全導致讀取的數據不一致,數據過期失效等問題。

使用那種第三方介質存放分散式鎖?

目前流行的是zookeeper和redis,兩者各有好處,redis流行的內存緩存,且能進行水平擴容同時還能提高請求負載,面對高並分散式鎖數據的讀寫請求能高速響應,同時有aof,哨兵機制可以防止某台宕機分散式鎖數據丟失帶來的問題。

zookeeper我是比較喜歡,因為他是分散式一致性演算法paxos演算法的實現,面對高負載請求毫無壓力,同時某一台宕機毫不影響分散式鎖數據一致性,且附帶了監聽機制,當某一程序釋放某一個鎖後,其他程序可以及時得到通知來獲得對該分散式鎖的控制權,這裡的輪詢實現不需要我們去開發了。

關於分散式鎖與線程鎖的介紹從一年前就在編輯中,一直沒有時間以一種通俗明了的方式介紹給大家。本人在很多論壇中發現很多剛入大數據領域的新人都會提到分散式鎖,但是並沒有深刻明白分散式鎖和線程鎖的場景,以至於很多情況下明明線程鎖就可以搞定的卻引入了分散式鎖,讓整個系統設計的更加複雜了。

另外要說的,zookeeper筆者認為是很棒的技術,雖然在大數據領域只是作為某一個框架的一個協調者出現,導致很多開發者忽視了他的偉大性。但是我想說的,在當前火熱的微服務中,其實會藉助zookeeper實現很多功能,比如分散式鎖,配置中心。

作者:劉洋

cnblogs.com/intsmaze/p/

推薦閱讀:

相关文章