任何努力都不能帶給你好運,可是它們能讓你悄悄地成爲更好的本身。html
在分佈式集羣系統的開發中,線程鎖每每並不能支持所有場景的使用,必須引入新的技術方案分佈式鎖。java
線程鎖:你們都不陌生,主要用來給方法、代碼塊加鎖。當某個方法或者代碼塊使用鎖時,那麼在同一時刻至多僅有有一個線程在執行該段代碼。當有多個線程訪問同一對象的加鎖方法/代碼塊時,同一時間只有一個線程在執行,其他線程必需要等待當前線程執行完以後才能執行該代碼段。可是,其他線程是能夠訪問該對象中的非加鎖代碼塊的。redis
進程鎖:也是爲了控制同一操做系統中多個進程訪問一個共享資源,只是由於程序的獨立性,各個進程是沒法控制其餘進程對資源的訪問的,可是可使用本地系統的信號量控制(操做系統基本知識)。算法
分佈式鎖:當多個進程不在同一個系統之中時,使用分佈式鎖控制多個進程對資源的訪問。數據庫
intsmaze說簡單點,實現分佈式鎖必需要依靠第三方存儲介質來存儲鎖的元數據等信息。好比分佈式集羣要操做某一行數據時,這個數據的流水號是惟一的,那麼咱們就把這個流水號做爲一把鎖的id,當某進程要操做該數據時,先去第三方存儲介質中看該鎖id是否存在,若是不存在,則將該鎖id寫入,而後執對該數據的操做;當其餘進程要訪問這個數據時,會先到第三方存儲介質中查看有沒有這個數據的鎖id,有的話就認爲這行數據目前已經有其餘進程在使用了,就會不斷地輪詢第三方存儲介質看其餘進程是否釋放掉該鎖;當進程操做完該數據後,該進程就到第三方存儲介質中把該鎖id刪除掉,這樣其餘輪詢的進程就能獲得對該鎖的控制。緩存
Redis中固然不能經過get,set操做判斷,get,set操做不是一個原子的,可使用redis的jedis.set(String key, String value, String nxxx, String expx, int time)命令來保證原子性。tomcat
具體實現方案: https://www.cnblogs.com/linji...
說了這麼多,再補充一點,線程鎖,進程鎖,分佈式鎖的做用都是同樣的,只是做用的範圍大小不一樣。範圍大小:分佈式鎖——大於——進程鎖——大於——線程鎖。能用線程鎖,進程鎖狀況下使用分佈式鎖也是能夠的,能用線程鎖的狀況下使用進程鎖也是能夠的。只是範圍越大技術複雜度就越大。服務器
關於分佈式鎖,有過javaEE開發經驗的就會說了,系統爲了應對高併發,會搭建一個好比tomcat集羣,集羣內服務都是訪問的同一臺數據庫,有多臺服務器同時修改同一條數據庫數據的操做,可是咱們並無在服務器中使用分佈式鎖?按照上面對分佈式鎖的解釋,兩個不一樣系統上的JVM進程同時訪問數據庫的同一個資源,這個時候咱們應該使用分佈式鎖進行控制。網絡
這說的沒有錯,可是咱們忘記了數據庫的特性了。若是兩臺服務器僅僅是直接訪問(經過url)並操做某臺服務器硬盤中某個文件同一行數據,這個時候咱們必須用分佈式鎖。可是由於這兩臺服務器訪問的數據是存儲在數據庫中的(數據庫自己就是一個服務程序,多線程的接收外部系統發來的請求),兩臺服務器的請求經過網絡IO發送到數據庫服務器後,而後把請求交給數據庫服務的進程處理,數據庫服務器是多線程接收請求並處理的,這個時候關於某表某一行數據的多線程訪問控制是由數據庫服務進行控制的(就是數據庫服務的代碼中進行了線程上的加鎖處理),這就是數據庫服務器的行鎖等特性,由於數據庫那一端已經對外部多個系統的請求進行了一個鎖操做,因此不須要咱們在應用服務端進行分佈式鎖的開發。mybatis
那若是想同時更新數據庫的多行數據,這個時候數據庫的行鎖就沒法保證了。這個時候咱們就要使用分佈式鎖,是的這個時候就可使用,注意我用的是能夠。爲何說能夠呢?由於數據庫自己就提供了這個機制,事務以及他的隔離級別。固然你也能夠不用數據庫提供的事務,用分佈式鎖。
分佈式鎖的設計並非徹底美好的,只能針對某些業務場景下使用,若是要對全部業務使用,必須充分理解業務需求合理的設計,至於緣由就和各位j2ee開發時mybatis的二級緩存以命名空間爲單位所要注意的業務問題時同樣的。
intsmaze使用分佈式鎖,咱們會把某表的第二第三行做爲id來鎖住,若是有相同的操做時更新該表第二第三行,咱們纔不讓他修改,必須讓他拿到鎖才能夠。可是若是有個操做僅僅是修改第二行,這個時候他就得到了對該行的操做,並且等數據庫釋放掉以前操做對該行的鎖後。因此分佈式鎖並非隨處可用的,只是在某些場景下可使用。好比業務系統不會存在單獨修改第二行的操做。
實際開發場景中,咱們會對hbase操做進行分佈式鎖,hbase做爲一款優秀的非內存數據庫,傳統數據庫同樣提供了事務的概念,只是HBase的事務是行級事務,能夠保證行級數據的原子性、一致性、隔離性以及持久性,即一般所說的ACID特性。爲了實現事務特性,HBase採用了各類併發控制策略,包括各類鎖機制、MVCC機制等。由於hbase只支持行級事物,當業務須要併發操做兩行甚至多行記錄時,hbase自己就沒法提供ACID的支持了。
數據庫會爲客戶端的每個請求建立一個線程,這些線程針對特定行數據修改必須得到該行的行鎖,而其餘客戶端線程要想修改該數據的話,必須等待前面的線程釋放鎖後才被容許。若是客戶端不少線程都要修改某行數據的話,沒有拿到鎖的線程都會在數據庫端機器上不斷輪詢,增大數據庫端的壓力。
咱們可使用分佈式鎖,把對數據庫行鎖的等待獲取的輪詢放到每個客戶端機器上去實現,這樣能夠避免數據庫端線程的不斷輪詢。好比,客戶端在要發送對數據庫的某行數據的操做請求前,在客戶端機器上進行鎖的爭搶,沒有獲取到鎖,就不會像數據庫端發送操做請求,這樣數據庫端就沒有了輪詢的壓力。固然分佈式鎖的引入必定要結合業務的需求來進行設計,否則會出現鎖id的命名不全致使讀取的數據不一致,數據過時失效等問題。
目前流行的是zookeeper和redis,二者各有好處,redis流行的內存緩存,且能進行水平擴容同時還能提升請求負載,面對高並分佈式鎖數據的讀寫請求能高速響應,同時有aof,哨兵機制能夠防止某臺宕機分佈式鎖數據丟失帶來的問題。
zookeeper我是比較喜歡,由於他是分佈式一致性算法paxos算法的實現,面對高負載請求毫無壓力,同時某一臺宕機絕不影響分佈式鎖數據一致性,且附帶了監聽機制,當某一程序釋放某一個鎖後,其餘程序能夠及時獲得通知來得到對該分佈式鎖的控制權,這裏的輪詢實現不須要咱們去開發了。
關於分佈式鎖與線程鎖的介紹從一年前就在編輯中,一直沒有時間以一種通俗明瞭的方式介紹給你們。本人在不少論壇中發現不少剛入大數據領域的新人都會提到分佈式鎖,可是並無深入明白分佈式鎖和線程鎖的場景,以致於不少狀況下明明線程鎖就能夠搞定的卻引入了分佈式鎖,讓整個系統設計的更加複雜了。
另外要說的,zookeeper筆者認爲是很棒的技術,雖然在大數據領域只是做爲某一個框架的一個協調者出現,致使不少開發者忽視了他的偉大性。可是我想說的,在當前火熱的微服務中,其實會藉助zookeeper實現不少功能,好比分佈式鎖,配置中心。