資料庫分布式鎖
1. 高並發沒鎖可不行,三種分布式鎖詳解
Java中的鎖主要包括synchronized鎖和JUC包中的鎖,這些鎖都是針對單個JVM實例上的鎖,對於分布式環境如果我們需要加鎖就顯得無能為力。在單個JVM實例上,鎖的競爭者通常是一些不同的線程,而在分布式環境中,鎖的競爭者通常是一些不同的線程或者進程。如何實現在分布式環境中對一個對象進行加鎖呢?答案就是分布式鎖。
目前分布式鎖的實現方案主要包括三種:
基於資料庫實現分布式鎖主要是利用資料庫的唯一索引來實現,唯一索引天然具有排他性,這剛好符合我們對鎖的要求:同一時刻只能允許一個競爭者獲取鎖。加鎖時我們在資料庫中插入一條鎖記錄,利用業務id進行防重。當第一個競爭者加鎖成功後,第二個競爭者再來加鎖就會拋出唯一索引沖突,如果拋出這個異常,我們就判定當前競爭者加鎖失敗。防重業務id需要我們自己來定義,例如我們的鎖對象是一個方法,則我們的業務防重id就是這個方法的名字,如果鎖定的對象是一個類,則業務防重id就是這個類名。
基於緩存實現分布式鎖:理論上來說使用緩存來實現分布式鎖的效率最高,加鎖速度最快,因為Redis幾乎都是純內存操作,而基於資料庫的方案和基於Zookeeper的方案都會涉及到磁碟文件IO,效率相對低下。一般使用Redis來實現分布式鎖都是利用Redis的 SETNX key value 這個命令,只有當key不存在時才會執行成功,如果key已經存在則命令執行失敗。
基於Zookeeper:Zookeeper一般用作配置中心,其實現分布式鎖的原理和Redis類似,我們在Zookeeper中創建瞬時節點,利用節點不能重復創建的特性來保證排他性。
在實現分布式鎖的時候我們需要考慮一些問題,例如:分布式鎖是否可重入,分布式鎖的釋放時機,分布式鎖服務端是否有單點問題等。
上面已經分析了基於資料庫實現分布式鎖的基本原理:通過唯一索引保持排他性,加鎖時插入一條記錄,解鎖是刪除這條記錄。下面我們就簡要實現一下基於資料庫的分布式鎖。
id欄位是資料庫的自增id,unique_mutex欄位就是我們的防重id,也就是加鎖的對象,此對象唯一。在這張表上我們加了一個唯一索引,保證unique_mutex唯一性。holder_id代表競爭到鎖的持有者id。
如果當前sql執行成功代表加鎖成功,如果拋出唯一索引異常(DuplicatedKeyException)則代表加鎖失敗,當前鎖已經被其他競爭者獲取。
解鎖很簡單,直接刪除此條記錄即可。
是否可重入 :就以上的方案來說,我們實現的分布式鎖是不可重入的,即是是同一個競爭者,在獲取鎖後未釋放鎖之前再來加鎖,一樣會加鎖失敗,因此是不可重入的。解決不可重入問題也很簡單:加鎖時判斷記錄中是否存在unique_mutex的記錄,如果存在且holder_id和當前競爭者id相同,則加鎖成功。這樣就可以解決不可重入問題。
鎖釋放時機 :設想如果一個競爭者獲取鎖時候,進程掛了,此時distributed_lock表中的這條記錄就會一直存在,其他競爭者無法加鎖。為了解決這個問題,每次加鎖之前我們先判斷已經存在的記錄的創建時間和當前系統時間之間的差是否已經超過超時時間,如果已經超過則先刪除這條記錄,再插入新的記錄。另外在解鎖時,必須是鎖的持有者來解鎖,其他競爭者無法解鎖。這點可以通過holder_id欄位來判定。
資料庫單點問題 :單個資料庫容易產生單點問題:如果資料庫掛了,我們的鎖服務就掛了。對於這個問題,可以考慮實現資料庫的高可用方案,例如MySQL的MHA高可用解決方案。
使用Jedis來和Redis通信。
可以看到,我們加鎖就一行代碼:
jedis.set(String key, String value, String nxxx, String expx, int time);
這個set()方法一共五個形參:
第一個為key,我們使用key來當鎖,因為key是唯一的。
第二個為value,這里寫的是鎖競爭者的id,在解鎖時,我們需要判斷當前解鎖的競爭者id是否為鎖持有者。
第三個為nxxx,這個參數我們填的是NX,意思是SET IF NOT EXIST,即當key不存在時,我們進行set操作;若key已經存在,則不做任何操作。
第四個為expx,這個參數我們傳的是PX,意思是我們要給這個key加一個過期時間的設置,具體時間由第五個參數決定;
第五個參數為time,與第四個參數相呼應,代表key的過期時間。
總的來說,執行上面的set()方法就只會導致兩種結果:1.當前沒有鎖(key不存在),那麼久進行加鎖操作,並對鎖設置一個有效期,同時value表示加鎖的客戶端。2.已經有鎖存在,不做任何操作。
上述解鎖請求中, SET_IF_NOT_EXIST (不存在則執行)保證了加鎖請求的排他性,緩存超時機制保證了即使一個競爭者加鎖之後掛了,也不會產生死鎖問題:超時之後其他競爭者依然可以獲取鎖。通過設置value為競爭者的id,保證了只有鎖的持有者才能來解鎖,否則任何競爭者都能解鎖,那豈不是亂套了。
解鎖的步驟:
注意到這里解鎖其實是分為2個步驟,涉及到解鎖操作的一個原子性操作問題。這也是為什麼我們解鎖的時候用Lua腳本來實現,因為Lua腳本可以保證操作的原子性。那麼這里為什麼需要保證這兩個步驟的操作是原子操作呢?
設想:假設當前鎖的持有者是競爭者1,競爭者1來解鎖,成功執行第1步,判斷自己就是鎖持有者,這是還未執行第2步。這是鎖過期了,然後競爭者2對這個key進行了加鎖。加鎖完成後,競爭者1又來執行第2步,此時錯誤產生了:競爭者1解鎖了不屬於自己持有的鎖。可能會有人問為什麼競爭者1執行完第1步之後突然停止了呢?這個問題其實很好回答,例如競爭者1所在的JVM發生了GC停頓,導致競爭者1的線程停頓。這樣的情況發生的概率很低,但是請記住即使只有萬分之一的概率,在線上環境中完全可能發生。因此必須保證這兩個步驟的操作是原子操作。
是否可重入 :以上實現的鎖是不可重入的,如果需要實現可重入,在 SET_IF_NOT_EXIST 之後,再判斷key對應的value是否為當前競爭者id,如果是返回加鎖成功,否則失敗。
鎖釋放時機 :加鎖時我們設置了key的超時,當超時後,如果還未解鎖,則自動刪除key達到解鎖的目的。如果一個競爭者獲取鎖之後掛了,我們的鎖服務最多也就在超時時間的這段時間之內不可用。
Redis單點問題 :如果需要保證鎖服務的高可用,可以對Redis做高可用方案:Redis集群+主從切換。目前都有比較成熟的解決方案。
利用Zookeeper創建臨時有序節點來實現分布式鎖:
其基本思想類似於AQS中的等待隊列,將請求排隊處理。其流程圖如下:
解決不可重入 :客戶端加鎖時將主機和線程信息寫入鎖中,下一次再來加鎖時直接和序列最小的節點對比,如果相同,則加鎖成功,鎖重入。
鎖釋放時機 :由於我們創建的節點是順序臨時節點,當客戶端獲取鎖成功之後突然session會話斷開,ZK會自動刪除這個臨時節點。
單點問題 :ZK是集群部署的,主要一半以上的機器存活,就可以保證服務可用性。
Zookeeper第三方客戶端curator中已經實現了基於Zookeeper的分布式鎖。利用curator加鎖和解鎖的代碼如下:
2. 鍒嗗竷寮忛攣鐨勫疄鐜版柟寮
鍒嗗竷寮忛攣鐨勫疄鐜版柟寮忎富瑕佸寘鎷鍩轟簬鏁版嵁搴撱佸熀浜庣紦瀛樼郴緇熴佸熀浜嶼ookeeper絳夊嚑縐嶆柟娉曘
棣栧厛錛屽熀浜庢暟鎹搴撶殑鍒嗗竷寮忛攣瀹炵幇錛屽彲浠ュ埄鐢ㄦ暟鎹搴撶殑鍞涓緔㈠紩鎴栦富閿綰︽潫鏉ュ疄鐜伴攣鐨勫姛鑳姐備緥濡傦紝鍦ㄦ暟鎹搴撲腑鍒涘緩涓涓鍖呭惈鍞涓緔㈠紩鐨勮〃錛屽綋闇瑕佸姞閿佹椂錛屽皾璇曞悜璇ヨ〃涓鎻掑叆涓鏉¤板綍銆傜敱浜庡敮涓緔㈠紩鐨勫瓨鍦錛屽彧鏈夌涓涓鎻掑叆鐨勬搷浣滆兘澶熸垚鍔燂紝鍚庣畫鎻掑叆鎿嶄綔灝嗚闃誨炴垨榪斿洖澶辮觸錛屼粠鑰屽疄鐜伴攣鐨勬晥鏋溿傝繖縐嶆柟寮忕殑浼樼偣鏄綆鍗曟槗琛岋紝浣嗙己鐐規槸鎬ц兘鍙鑳藉彈鍒版暟鎹搴撴ц兘鐨勫獎鍝嶏紝涓斿湪楂樺苟鍙戝満鏅涓嬪彲鑳借〃鐜頒笉浣熾
鍏舵★紝鍩轟簬緙撳瓨緋葷粺鐨勫垎甯冨紡閿侊紝濡俁edis錛屾槸鍙︿竴縐嶅父瑙佺殑瀹炵幇鏂瑰紡銆俁edis鎻愪緵浜唖etnx錛坰et if not exist錛夊懡浠わ紝鍙浠ュ師瀛愭у湴璁劇疆涓涓猭ey鐨勫礆紝騫惰繑鍥炴槸鍚﹁劇疆鎴愬姛銆傚埄鐢ㄨ繖涓鐗規э紝鍙浠ュ皢key浣滀負閿佺殑鏍囧織錛屽綋闇瑕佸姞閿佹椂錛屽皾璇曡劇疆璇key銆傚傛灉璁劇疆鎴愬姛錛屽垯琛ㄧず鑾峰彇鍒頒簡閿侊紱濡傛灉璁劇疆澶辮觸錛屽垯琛ㄧず閿佸凡琚鍏朵粬瀹㈡埛絝鍗犵敤銆傛ゅ栵紝Redis榪樻敮鎸佽劇疆key鐨勮繃鏈熸椂闂達紝浠庤岄伩鍏嶆婚攣鐨勬儏鍐點傝繖縐嶆柟寮忕殑浼樼偣鏄鎬ц兘杈冮珮錛岄傜敤浜庨珮騫跺彂鍦烘櫙錛屼絾闇瑕侀濆栫殑Redis鏈嶅姟鏀鎸併
鏈鍚庯紝鍩轟簬Zookeeper鐨勫垎甯冨紡閿佸疄鐜頒篃鏄涓縐嶅彲闈犵殑閫夋嫨銆俍ookeeper鏄涓涓涓哄垎甯冨紡搴旂敤鎻愪緵楂樻ц兘銆佸彲闈犮佹湁搴忕殑鍗忓悓鏈嶅姟鐨勫紑婧愰」鐩銆傚埄鐢╖ookeeper鐨勪復鏃惰妭鐐瑰拰watch鏈哄埗錛屽彲浠ュ疄鐜板垎甯冨紡閿併傚叿浣撳湴錛屽綋闇瑕佸姞閿佹椂錛屽湪Zookeeper鐨勬寚瀹氳妭鐐逛笅鍒涘緩涓涓涓存椂鑺傜偣浣滀負閿佺殑鏍囧織銆傜敱浜嶼ookeeper鐨勮妭鐐瑰叿鏈夊敮涓鎬э紝鍥犳ゅ彲浠ョ『淇濆悓涓鏃跺埢鍙鏈変竴涓瀹㈡埛絝鑳藉熷壋寤烘垚鍔熴傚悓鏃訛紝鍏朵粬瀹㈡埛絝鍙浠ラ氳繃watch鏈哄埗鐩戝惉璇ヨ妭鐐圭殑鍙樺寲錛屼粠鑰屽湪閿侀噴鏀炬椂鍙婃椂鑾峰彇鍒伴氱煡銆傝繖縐嶆柟寮忕殑浼樼偣鏄鍙闈犳ч珮銆佹敮鎸佸嶆潅鐨勫垎甯冨紡鍦烘櫙錛屼絾鐩稿規潵璇村疄鐜伴毦搴︾◢澶с
緇間笂鎵榪幫紝鍒嗗竷寮忛攣鐨勫疄鐜版柟寮忔湁澶氱嶏紝姣忕嶆柟寮忛兘鏈夊叾浼樼己鐐瑰拰閫傜敤鍦烘櫙銆傚湪瀹為檯搴旂敤涓錛岄渶瑕佹牴鎹鍏蜂綋闇奼傚拰緋葷粺鐜澧冮夋嫨鍚堥傜殑瀹炵幇鏂瑰紡銆傚悓鏃訛紝涓轟簡淇濊瘉鍒嗗竷寮忛攣鐨勬g『鎬у拰鎬ц兘錛岃繕闇瑕佹敞鎰忓勭悊鍚勭嶅紓甯告儏鍐點侀伩鍏嶆婚攣絳夐棶棰樸
3. 鍒嗗竷寮忛攣鍙婂叾甯歌佸疄鐜版柟寮
鍦ㄥ垎甯冨紡緋葷粺涓錛屼負浜嗕繚璇佸規暟鎹鐨勪慨鏀規湁鏈緇堜竴鑷存э紝閫氬父浣跨敤鍒嗗竷寮忛攣鎴栬呭垎甯冨紡浜嬪姟銆傛瘮濡傚父瑙佺殑澶氫釜緋葷粺鍚屾椂淇鏀瑰晢鍝侊紝鏃渚濊禆浜庣幇鏈夋暟鎹涔熻佷慨鏀規暟鎹錛屽傛灉娌℃湁闄愬埗錛岄珮騫跺彂鎯呭喌涓嬪緢鍙鑳芥渶緇堟暟鎹鏄閿欒鐨勩
涓庡崟鏈洪攣涓嶅悓錛屽垎甯冨紡閿佹洿鍔犲嶆潅錛岄渶瑕佽冭檻緗戠粶寤惰繜銆佹湇鍔¢樆濉炵瓑錛岄氬父鍏鋒湁濡備笅鐗圭偣錛
鍒╃敤鏁版嵁搴撲富閿鍞涓鐨勭壒鎬э紝鍙浠ュ熀浜庡敮涓涓婚敭淇濊瘉澶氭℃搷浣滃彧鏈変竴嬈℃垚鍔熴傚湪鏁版嵁搴撲腑鍒涘緩涓涓琛錛岃〃涓鍖呭惈鏂規硶鍚嶇瓑瀛楁碉紝騫跺湪鏂規硶鍚嶅瓧孌典笂鍒涘緩鍞涓緔㈠紩錛屾兂瑕佹墽琛屾煇涓鏂規硶錛屽氨浣跨敤榪欎釜鏂規硶鍚嶅悜琛ㄤ腑鎻掑叆鏁版嵁錛屾垚鍔熸彃鍏ュ垯鑾峰彇閿侊紝鎵ц屽畬鎴愬悗鍒犻櫎瀵瑰簲鐨勮屾暟鎹閲婃斁閿併傞噴鏀鵑攣鏃訛紝鐩存帴鍒犻櫎鏁版嵁搴撹板綍鍗沖彲銆
姝ゆ柟妗堝瓨鍦ㄧ殑闂棰樻槸寮轟緷璧栨暟鎹搴擄紝瀹規槗褰㈡垚鐑鐐癸紝鏁版嵁搴撻攣琛ㄥ艱嚧鐨勮秴鏃朵細褰卞搷鎬ц兘錛屾垨鑰呮暟鎹搴撳畷鏈轟細瀵艱嚧鏈嶅姟涓嶅彲鐢ㄣ傚苟涓旓紝鏁版嵁搴撴湰韜娌℃湁澶辨晥鏈哄埗錛屽傛灉浠誨姟宕╂簝浼氬艱嚧鏁版嵁搴撲腑鐨勯攣涓嶈兘琚閲婃斁銆傛暟鎹搴撴彃鍏ユ搷浣滄湰韜娌℃湁闃誨炴満鍒訛紝鏁呮棤娉曞疄鐜板垎甯冨紡閿佺殑闃誨炵瓑寰咃紝浠誨姟綰跨▼鍙鑳介渶瑕侀噸澶嶅皾璇曟彃鍏ャ傜敱浜庡敮涓涓婚敭鐨勫瓨鍦錛屾寔鏈夐攣鐨勭嚎紼嬩篃鏃犳硶閲嶅嶈幏寰楅攣錛屽叾浠栫嚎紼嬬珵浜夐攣鐨勮繃紼嬩腑涔熸棤娉曟牴鎹浼樺厛綰ц繘琛屽垎閰嶃
鍦ㄦ暟鎹搴撲腑涓鴻〃澧炲姞涓涓鐗堟湰鍙峰瓧孌碉紝姣忔℃搷浣滄椂鍒ゆ柇鐗堟湰鍙鳳紝鍙鏈夌増鏈鍙蜂竴鑷存墠鑳借繘琛屽瑰簲鐨勪慨鏀癸紝淇鏀瑰悗鐗堟湰鍙峰姞 1錛岄氳繃 CAS 鐨勬柟寮忚繘琛屼慨鏀廣
姝ゅ疄鐜頒細澧炲姞鏁版嵁搴撴搷浣滅殑嬈℃暟錛岄珮騫跺彂鎯呭喌涓嬪彲鑳芥ц兘涓嶅ソ銆
for update鏄涓縐嶈岀駭閿侊紝鍙堝彨鎺掑畠閿侊紝涓鏃︾敤鎴峰規煇涓琛屾柦鍔犱簡琛岀駭鍔犻攣錛屽垯璇ョ敤鎴峰彲浠ユ煡璇涔熷彲浠ユ洿鏂拌鍔犻攣鐨勬暟鎹琛岋紝鍏跺畠鐢ㄦ埛鍙鑳芥煡璇浣嗕笉鑳芥洿鏂拌鍔犻攣鐨勬暟鎹琛屻傛垜浠鍙浠ヨや負鑾峰緱鎺掍粬閿佺殑綰跨▼鍗寵幏寰楀垎甯冨紡閿侊紝浠誨姟鎵ц屽畬鎴愬悗閫氳繃 commit 鏉ラ噴鏀鵑攣銆俧or update 璇鍙ヤ細鍦ㄦ墽琛屾垚鍔熷悗絝嬪嵆榪斿洖錛屽湪鎵ц屽け璐ユ椂涓鐩村勪簬闃誨炵姸鎬侊紝鐩村埌鎴愬姛銆
浣嗘槸 MySQL 浼氬規煡璇㈣繘琛屼紭鍖栵紝鍗充究鍦ㄦ潯浠朵腑浣跨敤浜嗙儲寮曞瓧孌碉紝浣嗘槸鍚︿嬌鐢ㄧ儲寮曟潵媯緔㈡暟鎹鏄鐢 MySQL 閫氳繃鍒ゆ柇涓嶅悓鎵ц岃″垝鐨勪唬浠鋒潵鍐沖畾鐨勶紝濡傛灉 MySQL 璁や負鍏ㄨ〃鎵鏁堢巼鏇撮珮錛屾瘮濡傚逛竴浜涘緢灝忕殑琛錛屽畠灝變笉浼氫嬌鐢ㄧ儲寮曪紝榪欑嶆儏鍐典笅 InnoDB 灝嗕嬌鐢ㄨ〃閿侊紝鑰屼笉鏄琛岄攣銆
setnx 鐨勫惈涔夊氨鏄 SET if Not Exists錛屼富瑕佹湁涓や釜鍙傛暟 setnx(key, value)銆傝ユ柟娉曟槸鍘熷瓙鐨勶紝濡傛灉 key 涓嶅瓨鍦錛屽垯璁劇疆褰撳墠 key 鎴愬姛錛岃繑鍥 1錛涘傛灉褰撳墠 key 宸茬粡瀛樺湪錛屽垯璁劇疆褰撳墠 key 澶辮觸錛岃繑鍥 0銆俿etnx 鍛戒護涓嶈兘璁劇疆 key 鐨勮秴鏃舵椂闂達紝鍙鑳介氳繃 expire() 鏉ヨ劇疆銆
閿佺殑瀹炵幇姝ラわ細
榪欎釜鏂規堝傛灉鍦ㄧ涓姝 setnx 鎵ц屾垚鍔熷悗錛屽湪 expire() 鍛戒護鎵ц屾垚鍔熷墠錛屽彂鐢熶簡瀹曟満鐨勭幇璞★紝閭d箞灝變緷鐒朵細鍑虹幇姝婚攣鐨勯棶棰樸
榪欎釜鏂規堟槸瀵逛笂涓涓鏂規堢殑浼樺寲鐗堟湰銆
getset() 鍛戒護涓昏佹湁涓や釜鍙傛暟 getset(key錛宯ewValue)銆傝ユ柟娉曟槸鍘熷瓙鐨勶紝瀵 key 璁劇疆 newValue 榪欎釜鍊礆紝騫朵笖榪斿洖 key 鍘熸潵鐨勬棫鍊箋傚亣璁 key 鍘熸潵鏄涓嶅瓨鍦ㄧ殑錛岄偅涔堥栨℃墽琛岀殑榪斿洖鍊兼槸 null銆
閿佺殑瀹炵幇姝ラわ細
榪欎釜鏂規堝湪浠誨姟澶勭悊瓚呮椂鎴栧彂鐢熷畷鏈烘椂錛屾棤闇鎷呭績閿佽秴鏃墮棶棰橈紝涓嬫¤鋒眰鍙浠ュ垽鏂鍑哄疄闄呬笂閿佸凡緇忚秴鏃朵簡銆
zookeeper 鐢卞氫釜鑺傜偣鏋勬垚錛堝崟鏁幫級錛岄噰鐢 zab 涓鑷存у崗璁銆傚洜姝ゅ彲浠ュ皢 zk 鐪嬫垚涓涓鍗曠偣緇撴瀯錛屽瑰叾淇鏀規暟鎹鍏跺唴閮ㄨ嚜鍔ㄥ皢鎵鏈夎妭鐐規暟鎹榪涜屼慨鏀硅屽悗鎵嶆彁渚涙煡璇㈡湇鍔°
zookeeper 鏁版嵁鏄鐩褰曟爲鐨勫艦寮忥紝姣忎釜鐩褰曠О涓 znode錛 znode 涓鍙瀛樺偍鏁版嵁錛堜竴鑸涓嶈秴榪 1M錛夛紝榪樺彲浠ュ湪鍏朵腑澧炲姞瀛愯妭鐐廣
瀛愯妭鐐規湁涓夌嶇被鍨嬨
zookeeper 鎻愪緵浜 Watch 鏈哄埗錛宑lient 鍙浠ョ洃鎺ф瘡涓鑺傜偣鐨勫彉鍖栵紝褰撲駭鐢熷彉鍖栦細緇 client 浜х敓涓涓浜嬩歡銆
鍙浠ュ埄鐢ㄤ復鏃惰妭鐐逛笌 watch 鏈哄埗瀹炵幇鍒嗗竷寮忛攣銆傛瘡涓閿佸崰鐢ㄤ竴涓鏅閫氳妭鐐 /lock錛屽綋闇瑕佽幏鍙栭攣鏃跺湪 /lock 鐩褰曚笅鍒涘緩涓涓涓存椂鑺傜偣錛屽壋寤烘垚鍔熷垯琛ㄧず鑾峰彇閿佹垚鍔燂紝澶辮觸鍒 watch/lock 鑺傜偣錛屾湁鍒犻櫎鎿嶄綔鍚庡啀鍘諱簤閿併備復鏃惰妭鐐瑰ソ澶勫湪浜庡綋榪涚▼鎸傛帀鍚庨攣鐨勮妭鐐硅嚜鍔ㄥ垹闄や笉浼氬彂鐢熸婚攣銆
緙虹偣鍦ㄤ簬鎵鏈夊彇閿佸け璐ョ殑榪涚▼閮界洃鍚鐖惰妭鐐癸紝寰堝規槗鍙戠敓緹婄兢鏁堝簲錛屽嵆褰撻噴鏀鵑攣鍚庢墍鏈夌瓑寰呰繘紼嬩竴璧鋒潵鍒涘緩鑺傜偣錛屽苟鍙戦噺寰堝ぇ銆
涓涓鍙琛岀殑浼樺寲鏂規堟槸涓婇攣鏀逛負鍒涘緩涓存椂鏈夊簭鑺傜偣錛屾瘡涓涓婇攣鐨勮妭鐐瑰潎鑳藉壋寤鴻妭鐐規垚鍔燂紝鍙鏄鍏跺簭鍙蜂笉鍚屻傚彧鏈夊簭鍙鋒渶灝忕殑鍙浠ユ嫢鏈夐攣錛屽傛灉榪欎釜鑺傜偣搴忓彿涓嶆槸鏈灝忕殑鍒 watch 搴忓彿姣旀湰韜灝忕殑鍓嶄竴涓鑺傜偣 (鍏騫抽攣)銆倃atch 浜嬩歡鍒版潵鍚庯紝鍐嶆″垽鏂鏄鍚﹀簭鍙鋒渶灝忋傚彇閿佹垚鍔熷垯鎵ц屼唬鐮侊紝鏈鍚庨噴鏀鵑攣錛堝垹闄よヨ妭鐐癸級銆
鎬ц兘涓婂彲鑳芥病鏈夌紦瀛樻湇鍔¢偅涔堥珮錛屽洜涓烘瘡嬈″湪鍒涘緩閿佸拰閲婃斁閿佺殑榪囩▼涓錛岄兘瑕佸姩鎬佸壋寤恆侀攢姣佷復鏃惰妭鐐規潵瀹炵幇閿佸姛鑳姐倆ookeeper 涓鍒涘緩鍜屽垹闄よ妭鐐瑰彧鑳介氳繃 Leader 鏈嶅姟鍣ㄦ潵鎵ц岋紝鐒跺悗灝嗘暟鎹鍚屾ュ埌鎵鏈夌殑 Follower 鏈哄櫒涓娿
鍒嗗竷寮忛攣姣旇緝澶嶆潅錛屼篃姣旇緝瀹規槗鍙戠敓姝婚攣銆傜洰鍓嶄富嫻佺殑瀹炵幇鏂瑰紡鍖呮嫭錛
鍒嗗竷寮忛攣鍙婂叾甯歌佸疄鐜版柟寮 - 紼嬪簭涔嬪績