今天聊聊分布式鎖 No.86

      網友投稿 769 2022-05-29

      好了切入正題,一直在工作中會聊到很多鎖的問題,今天跟大家一起閑聊一下,究竟什么是鎖,為什么需要鎖,以及分布式的情況下,怎么設計和實現鎖。

      什么是鎖?

      明·魏禧《大鐵椎傳》上是這樣解釋的:

      鎖:置于可啟閉的器物上,以鑰匙或暗碼(如字碼機構、時間機構、自動釋放開關、磁性螺線管等)打開的扣件,例如:柄鐵折疊環復,如鎖上練,引之長丈許。

      鎖,就是要對一個可啟閉的東西上,擁有者擁有著鑰匙或者某些 Code , 用于打開的扣件。那么鎖為什么要產生?為什么要用鎖來將那個東西給加上鎖,以便達到只有擁有者可以操作的效果呢?

      歷史上來看,鎖幾乎與私有制同時誕生。早在公元前3000年的中國仰韶文化遺址中,就留存有裝在木結構框架建筑上的木鎖。東漢時,中國鐵制三簧鎖的技術已具有相當高的水平。三簧鎖前后沿用了1000多年。

      那么在互聯網,在軟件中的鎖是什么定義呢?在我看來,鎖就是保證多線程在競態條件下對共享資源操作的一致性。

      今天聊聊分布式鎖 No.86

      怎么理解?

      如果沒有共享資源,那么鎖并沒有任何作用,每個業務每個線程都擁有自己的獨占的資源,那么鎖也就沒有用武之地了。這些資源,任何其他業務其他線程都訪問不了,那么這些資源對于本業務來說就是私有的,也就不需要加鎖了。

      那什么叫競態條件呢?百科里是這樣解釋的:

      競態條件(race condition),從多進程間通信的角度來講,是指兩個或多個進程對共享的數據進行讀或寫的操作時,最終的結果取決于這些進程的執行順序。

      我們可以抓住三個關鍵字,多進程、共享數據、執行順序。如果并沒有多進程多線程,那么并不需要鎖,因為不可能會出現競態。如果所有的操作都是有序的,那么也不需要鎖,因為順序操作只要每個操作都是原子性的,那么基本不可能會出現競態。

      所以,鎖的出現,是為了保證多線程在競態條件下對共享資源操作的一致性。

      經典傳統應用環境下鎖的使用機制是怎么樣的?我們都知道數據庫有很多種鎖。樂觀鎖,悲觀鎖,排他鎖,行鎖,表鎖... 諸如此類的定義。我們這里只稍微看樂觀鎖和悲觀鎖。

      樂觀鎖:很樂觀。認為大家的數據操作都是很守規矩的不會亂來,所以只在修改操作的時候會加鎖。

      悲觀鎖:很悲觀。認為大家的數據操作都是不可估計而且可能帶來嚴重影響的,所以在整個操作過程都會進行加鎖。

      當然,各種設計可能在這個層次之上會加上意向鎖,意思就是你要獲得樂觀鎖之前,要先獲得意見樂觀鎖,意向排他鎖與此類似。

      也可能很變態,帶上意向的意向鎖。就好像,預約一下去預約去預約買車牌的預約。

      傳統的應用因為都是單機的,所以可以單起一個線程單獨控制所有的操作即可,對數據進行鎖定。很多的數據庫都實現了相應的鎖機制。

      例如 Oracle ,根據保護的對象不同,Oracle實現的數據庫鎖可以分為以下幾大類。

      1、DML鎖(data locks,數據鎖),用于保護數據的完整性。

      2、DDL鎖(dictionary locks,字典鎖),用于保護數據庫對象的結構,如表、索引等的結構定義。

      3、內部鎖和閂(internal locks and latches),保護數據庫的內部結構。

      例如 MySQL ,不同的引擎支持的鎖類型是不一樣的,下面的表格可以一探究竟。至于樂觀、悲觀、意向樂觀、意向悲觀這些的設計跟 Oracle 如出一轍。

      但是慢慢的,很多軟件都運行在分布式的環境下,具體的套路可以看看我之前的文章。分布式架構的套路No.74

      那為什么需要在分布式環境下使用鎖呢?傳統的應用在單機的情況下直接用一個統一的線程進行管控就可以了,但是在分布式環境下情況又不一樣了。如果每個人都只持有自己的鎖,對于其他人不可見,并不是全局唯一的鎖,這樣的鎖是沒有意義的。所以也就會有了分布式架構下的鎖。

      分布式鎖有兩層含義。第一層是在分布式的系統中用鎖來保證業務的正確性。另外一層是用分布式的服務來保證鎖的高可用性。

      在分布式的環境中,分布式鎖的實現方式大概有下面這么幾種。

      數據庫。

      緩存。

      分布式一致性系統。

      下面我們一一來聊他們的設計和實現。

      數據庫

      現在 MySQL 和很多數據庫都實現了分布式,但是也可以使用 MySQL 自己來實現分布式鎖,實現方式是這樣的。

      1、在分布式操作之前,對數據庫的定義了唯一鍵的表中插入一條數據。

      2、操作之后,將這條數據刪除掉。

      3、啟動一個定時 Job ,對已經過時的鎖進行刪除。

      這樣就能實現一個基于數據庫的排他鎖了。

      緩存系統

      緩存系統在實現的時候跟數據庫的模式差不多,但是因為數據都是在緩存中,所以加鎖和解鎖都會比數據庫快很多。

      下面舉例看看基于 Redis 的分布式鎖實現。Redis 的分布式鎖都是基于一個命令 -- SETNX,也就是 SET IF NOT EXIST,如果不存在就寫入。從 Redis 2.6.12 版本開始,Redis 的 SET 命令直接直接設置 NX 和 EX 屬性,NX 即附帶了?SETNX?數據,key 存在就無法插入,EX 是過期屬性,可以設置過期時間。這樣一個命令就能原子的完成加鎖和設置過期時間。

      第一次放廣告,試試了,別打我哈:

      代碼在這,自己看看吧。

      pom文件是這樣。

      實現的代碼是這樣的。我就不講解了,Code will talk 。

      lockredis.clients.jedis.Jedisredis.clients.jedis.JedisPoolredis.clients.jedis.JedisPoolConfigutils.Printerjava.util.Collectionsjava.util.ResourceBundleRedisManager?{ ???JedisPool?String?=?String?=?String?=?Long?=?{ ??????ResourceBundle?resourceBundle?=?ResourceBundle.()maxActive?=?Integer.(resourceBundle.getString())maxIdle?=?Integer.(resourceBundle.getString())maxWait?=?Integer.(resourceBundle.getString())String?ip?=?resourceBundle.getString()port?=?Integer.(resourceBundle.getString())JedisPoolConfig?config?=?JedisPoolConfig()config.setMaxTotal(maxActive)config.setMaxIdle(maxIdle)config.setMaxWaitMillis(maxWait)=?JedisPool(configipport)} ???(String?keyString?valueexpireSecond){ ??????Jedis?jedis?=?.getResource()(jedis?==?){ ?????????} ??????String?result?=?jedis.set(keyvalueexpireSecond)(.equals(result))?{ ?????????} ??????} ???(String?keyString?value)?{ ??????Jedis?jedis?=?.getResource()(jedis?==?){ ?????????} ??????String?script?=?Object?result?=?jedis.eval(scriptCollections.(key)Collections.(value))(.equals(result))?{ ?????????} ??????} ???(String[]?args){ ??????Printer.(())Printer.(())} }

      那么基于 Tair 的分布式鎖是怎么實現的呢?

      Tair 多了一個版本的概念,所以另外一種實現思路是用版本來控制鎖。加鎖的時候寫一個默認的版本號,那么如果兩次寫入都指定了同一個版本的話,服務端會直接報錯導致加鎖失敗。

      分布式一致性系統

      分布式一致性的系統,現在最流行的應該局勢 Zookeeper 了。Zookeeper 實現了類 Paxos 的設計。用 Zookeeper 是使用新增子節點的模式來進行加鎖。

      比如 B 要對數據 A 進行加鎖,可以這樣操作。 ?create /locks/A "B"

      其他節點在對 Zookeeper 進行加鎖的時候,因為目錄已經存在,會直接報錯。解鎖的時候直接 ? ?delete?/locks/A ?,這樣就好了。

      Zookeeper 是怎么實現分布式一致性的呢?最最主要的設計就是 Zookeeper 實現了 leader 選舉制以及 follower 轉發制。follower 在接收到請求的時候,會直接轉發給 leader,由 leader 進行數據的統一處理。

      https://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247484245&idx=1&sn=53d2eb4e2a3fd28ed9c28445a3aeb17f&chksm=9bed2227ac9aab314afaeed217b0c8d352f3a053fc239c95d9d888dd74bb5d3bf4741de910e1&scene=21#wechat_redirect

      分布式 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:python+requests進行get、post方法接口測試
      下一篇:Android 面試題之ContentProvider使用+實例
      相關文章
      久热综合在线亚洲精品| 久久久亚洲精华液精华液精华液 | 亚洲欧洲久久av| 亚洲AV日韩AV无码污污网站| 亚洲国产视频久久| 亚洲欧洲国产经精品香蕉网| 亚洲小说图片视频| 亚洲成电影在线观看青青| 亚洲日产韩国一二三四区| 亚洲色www永久网站| 亚洲色大成网站www尤物| 一本色道久久综合亚洲精品蜜桃冫| 亚洲一区二区三区在线观看蜜桃 | 亚洲欧洲日韩国产| 亚洲酒色1314狠狠做| 久久亚洲sm情趣捆绑调教| 亚洲精品电影在线| 亚洲人成网站在线观看播放动漫| 91亚洲性爱在线视频| 亚洲娇小性色xxxx| 亚洲性色AV日韩在线观看| 亚洲国产精品18久久久久久| 亚洲国产成人精品无码区花野真一| 亚洲av永久无码天堂网| 一本色道久久88亚洲综合| 亚洲午夜精品一级在线播放放| 亚洲免费无码在线| 77777亚洲午夜久久多喷| 亚洲日韩精品国产一区二区三区| 亚洲精品无码人妻无码| 成人精品国产亚洲欧洲| 国产亚洲欧洲Aⅴ综合一区 | 亚洲中文字幕无码中文字在线| 亚洲精品夜夜夜妓女网| 久久精品国产亚洲av日韩| 亚洲av无码一区二区三区四区| 在线观看亚洲电影| 中文字幕亚洲无线码| 亚洲AV无码成人网站久久精品大| 亚洲综合亚洲国产尤物| 亚洲AV成人影视在线观看|