問題的提出
在計算機科學領域,分布式一致性問題是一個相當重要,且被廣泛探索與論證的問題,通常存在於諸如分布式文件繫統、緩存繫統和數據庫等大型分布式存儲繫統中。
什麼是分布式一致性?分布式一致性分為哪些類型?分布式繫統達到一致性後將會是一個什麼樣的狀態?如果失去了一致性約束,分布式繫統是否還可以依賴?如果一味地追求一致性,對繫統的整體架構和性能又有多大影響?這一繫列的問題,似乎都沒有一個嚴格意義上準確的定義和答案。
終端用戶
IT技術的發展,讓我們受益無窮,從日常生活的超市收銀,到高端精細的火箭發射,現代社會中幾乎所有行業,都離不開計算機技術的支持。
盡管計算機工程師們創造出了很多高科技的計算機產品來解決我們日常踫到的問題,但用戶隻會傾向於選擇一些易用、好用的產品,那些難以使用的計算機產品終都會被淘汰——這種易用性,其實就是用戶體驗的一部分。
計算機產品的用戶體驗,可以分為便捷性、安全性和穩定性等方面。在本書中,我們主要討論的是用戶在使用計算機產品過程中遇到的那些和一致性有關的問題。在此之前,我們首先來看一下計算機產品的終端用戶是誰,他們的需求又是什麼。
火車站售票
假如說我們的終端用戶是一位經常做火車的旅行家,通常他是去車站的售票處購買車票,然後拿著車票去檢票口,再坐上火車,開始一段美好的旅行——一切似乎都是那麼和諧。想像一下,如果他選擇的目的地是杭州,而某一趟開往杭州的火車隻剩下一張車票了,可能在同一時刻,不同售票窗口的另一位乘客也購買了同一張車票。假如說售票繫統沒有進行一致性保障,兩人都購票成功了。而在檢票口檢票的時候,其中一位乘客會被告知他的車票無效——當然,現代的中國鐵路售票繫統已經很少出現這樣的問題了,但在這個例子中,我們可以看出,終端用戶對於我們的繫統的需求非常簡單:
“請售票給我,如果沒有餘票了,請在售票的時候就告訴我票是無效票的。”
這就對購票繫統提出了嚴格的一致性要求——繫統的數據(在本例中指的就是那趟開往杭州的火車的餘票數),無論在哪個售票窗口,每時每刻都必須是準確無誤的!
銀行轉賬
假如說我們的終端用戶是一名剛畢業的大學生,通常在拿到首月工資之後,都會選擇向家裡彙款。當他來到銀行櫃臺,完成轉賬操作後,銀行的櫃臺服務員會友善地提醒他:“您的轉賬將在N個工作日後到賬!”此時這名畢業生有一些沮喪,會對那名櫃臺服務員叮囑:“好吧,多久沒關繫,錢不要少就行了!”——這也成為了幾乎所有的用戶對於現代銀行繫統基本的需求。
網上購物
假如說我們的終端用戶是一名網上購物狂,當他看到一件庫存量為5的心儀商品,會迅速地確認購買,寫下收貨地址,然後下單——然而,在下單的那個瞬間,繫統可能會告知該用戶:“庫存量不足!”此時,絕大部分的消費者往往都會抱怨自己動作太慢,使得心愛的商品被其他人搶走了!
但其實有過網購繫統開發經驗的工程師一定明白,在商品詳情頁面上顯示的那個庫存量,通常不是該商品的真實庫存量,隻有在真正下單購買的時候,繫統纔會檢查該商品的真實庫存量。但是,誰在意呢?
在上面三個例子中,相信讀者一定已經看出來了,我們的終端用戶在使用不同的計算機產品時對於數據一致性的需求是不一樣的:
有些繫統,既要快速地響應用戶,同時還要保證繫統的數據對於任意客戶端都是真實可靠的,就像火車站的售票繫統。
還有些繫統,需要為用戶保證可靠的數據安全,雖然在數據一致性上存在延時,但結果務必保證嚴格的一致,就像銀行的轉賬繫統。
另外的一些繫統,雖然向用戶展示了一些可以說是“錯誤”的數據,但是在整個繫統使用過程中,一定會在某一個流程上對繫統數據進行準確無誤的檢查,從而避免用戶發生不必要的損失,就像網購繫統。
更新的並發性
在計算機發展的早期階段,受到底層硬件技術的制約,同時也是由於人們對於計算機繫統的實際使用需求比較簡單,因此很多上層的應用程序架構都是單線程模型的。以C語言為例,其誕生於上世紀70年代,當時幾乎所有使用C語言開發的應用程序都是單線程的。從現在來看,單線程應用程序雖然在運行效率上無法和後來的多線程應用程序相比,但是在編程模型上相對簡單,因此能夠避免多線程程序中出現的不少並發問題。
隨著計算機底層硬件技術和現代操作繫統的不斷發展,多線程技術開始被越來越多地引入到計算機編程模型之中,並對現代計算機應用程序的整體架構起到了至關重要的作用。
多線程的引入,為應用程序帶來性能上的卓越提升,同時也帶來了一個很大的副作用,那就是並發。《深入理解計算機繫統》一書對並發進行了如下定義:如果邏輯控制流在時間上重疊,那麼它們就是並發的。這裡提到的邏輯控制流,通俗地講,就是一次程序操作,比如讀取或更新內存中變量的值。
在本書後面的討論中,我們提到的“並發”都特指更新操作的並發,即有多個線程同時更新內存中變量的值——我們將這一現像稱為更新的並發性。
分布式一致性問題
在分布式繫統中另一個需要解決的重要問題就是數據的復制。在我們日常的開發經驗中,相信很多開發人員都踫到過這樣的問題:假設客戶端C1將繫統中的一個值K由V1更新為V2,但客戶端C2無法立即讀取到K的值,需要在一段時間之後纔能讀取到。讀者可能也已經猜到了,上面這個例子就是常見的數據庫之間復制的延時問題。
分布式繫統對於數據的復制需求一般都來自於以下兩個原因。
為了增加繫統的可用性,以防止單點故障引起的繫統不可用。
提高繫統的整體性能,通過負載均衡技術,能夠讓分布在不同地方的數據副本都能夠為用戶提供服務。
數據復制在可用性和性能方面給分布式繫統帶來的巨大好處是不言而喻的,然而數據復制所帶來的一致性挑戰,也是每一個繫統研發人員不得不面對的。
所謂的分布式一致性問題,是指在分布式環境中引入數據復制機制後,不同數據節點間可能出現的,並無法依靠計算機應用程序自身解決的數據不一致情況。簡單地講,數據一致性就是指在對一個副本數據進行更新的同時,必須確保也能夠更新其他的副本,否則不同副本之間的數據將不再一致。
那怎麼來解決這個問題呢?順著上面提到的復制延時問題,很快就有人想到了一種解決辦法,那就是:
“既然是由於延時引起的問題,那我可以將寫入的動作阻塞,直到數據復制完成後,纔完成寫入動作。”
沒錯,這似乎能解決問題,而且有一些繫統的架構也確實直接使用了這個思路。但這個思路在解決一致性問題的同時,又帶來了新的問題:寫入的性能。如果你的應用場景有非常多的寫請求,那麼使用這個思路之後,後續的寫請求都將會阻塞在前一個請求的寫操作上,導致繫統整理性能急劇下降。
總的來講,我們無法找到一種能夠滿足分布式繫統所有繫統屬性的分布式一致性解決方案。因此,如何既保證數據的一致性,同時又不影響繫統運行的性能,是每一個分布式繫統都需要重點考慮和權衡的。於是,一致性級別由此誕生。
強一致性
這種一致性級別是符合用戶直覺的,它要求繫統寫入什麼,讀出來的也會是什麼,用戶體驗好,但實現起來往往對繫統的性能影響比較大。
弱一致性
這種一致性級別約束了繫統在寫入成功後,不承諾立即可以讀到寫入的值,也不具體承諾多久之後數據能夠達到一致,但會盡可能地保證到某個時間級別(比如秒級別)後,數據能夠達到一致狀態。弱一致性還可以再進行細分:
會話一致性:該一致性級別隻保證對於寫入的值,在同一個客戶端會話中可以讀到一致的值,但其他的會話不能保證。
用戶一致性:該一致性級別隻保證對於寫入的值,在同一個用戶中可以讀到一致的值,但其他用戶不能保證。
本書將會從分布式一致性的理論出發,向讀者講解幾種典型的分布式一致性協議是如何解決分布式一致性問題的。之後,本書則會深入介紹分布式一致性問題的工業解決方案——ZooKeeper,並著重向讀者展示這一分布式協調框架的使用方法、內部實現以及運維技巧。
致謝
首先要感謝現在的部門老大蔣江偉先生。首次接觸蔣江偉是在2011年,當時參加了他的一個講座“淘寶前臺繫統優化實踐——吞吐量優化”,對其中關於“編寫GC友好代碼”的內容有不解之處,於是私下請教。他耐心的講解令我至今記憶猶新。兩年前,他全面負責中間件團隊之後,給予了我更大的幫助和鼓勵,使我得到了極大的進步,真的非常感謝。本書的問世,離不開他的推薦。也正是這一份寫作的責任感,讓我有決心和毅力來對整個ZooKeeper內容進行了一次全面的整理。在這裡,衷心祝福蔣江偉先生帶領中間件團隊走向新的高度。
其次,本書的寫作,離不開各位小伙伴們的支持和幫助,他們是各領域的資深專家,我向他們征集了很多有營養的內容。在這裡,按照章節順序,依次表示感謝:許澤彬參與了“問題提出”的寫作;侯前明對Paxos算法的前世今生進行的整理;段培樂對晦澀的Paxos協議進行了細致的講解;姜宇向我提供了他對於分布式事務的見解;徐偉辰參與了分布式鎖服務Chubby相關的寫作;葉成旭提供了他在上家公司時對Hypertable的學習和研究成果;高偉細致地向我展示了Curator這一ZooKeeper客戶端的使用;陳傑提供了他在“自動化的DNS服務”場景中的經驗總結;曹龍參與了Hadoop相關內容的寫作;鄧明鋻則貢獻了他對HBase的深刻見解;作為產品的開源負責人,莊曉丹和王強提供了對消息中間件Metamorphosis技術架構的講解;李鼎則向我全面展示了RPC服務框架Dubbo的技術細節;樓江航向我提供了Canal和Otter這兩個分布式產品中的ZooKeeper應用場景;李雨前、柳明和溫朝凱則一起寫了終搜在產品演進過程中對ZooKeeper的使用和改進;封仲淹參與了對其自主產品JStorm的技術剖析……是你們一遍又一遍地對內容進行修改,纔使得本書內容更為豐滿。
另外,也要感謝溫文鎏、王林、許澤彬、高偉和段培樂等人對全書的審閱,正是你們提出的寶貴建議,對完善本書提供了非常大的幫助。
感謝現在的同事陸學慧先生,從2013年下半年開始,他全面接手對ZooKeeper的開發和運維,在他身上感受到的專業和創新精神讓我備受鼓舞。
另外,感謝我的主管馬震先生,是他的幫助為我指引了方向,讓我有機會進入ZooKeeper的世界,並負責這個產品在公司的發展。盡管由於業務調整,馬震先生已經轉崗到其他部門,但依然由衷祝福他工作順利。
還要感謝我的同事,阿裡巴巴店鋪平臺的侯前明先生。本來該書作者應該是我們兩個人,但是由於期間他的家庭又增加了一個小生命,導致其不得不中途退出。從本書的選題到寫作大綱的制定,他都傾注了不少心血,相信如果有他一起創作,本書內容會更加豐滿、深刻。這裡表達遺憾的同時,也向這位兩個孩子的父親送去祝福,祝願他生活美滿。
感謝本書的責任編輯劉芸女士,是她反復審稿和編排,纔能讓本書的內容趨於完美。
感謝本書的封面設計吳海燕女士,她的努力已經無需言表,在技術書上的這一前衛、極富視覺衝擊力的封面設計,深深震撼到了我,也希望讀者朋友們能夠喜歡。
尤其感謝本書的策劃編輯張春雨先生。作為一個南方人,我很少有機會和那些有著一口北方腔的朋友交談,首次接到張春雨先生電話的時候,我纔真正領略了北京腔,也正是他的邀請,纔能讓我有機會進行本書的撰寫,同時在前後將近1年半的漫長寫作過程中,也是他的幫助和鼓勵,纔讓我堅持完成並不斷完善本書的內容。在這裡,也衷心祝願張春雨先生事業更上一層樓。
還有我的父母,在過去的1年時間裡,多次放假沒有回家,盡管父母一直鼓勵我專注工作,專注於自己的事業,但我深知他們內心對兒子的牽掛,在這裡也深深地向他們道一聲:“謝謝”,也謹以此書獻給我親愛的爸爸媽媽。
倪超
2014年12月於杭州淘寶城