Base: 一種 Acid 的替代方案

遷移到:http://www.bdata-cap.com/newsinfo/1741386.htmlhtml

原文連接: BASE: An Acid Alternative
Pdf下載連接: Base算法

數據庫 ACID,都不陌生:原子性、一致性、隔離性和持久性,這在單臺服務器就能搞定的時代,很容易實現,可是到了如今,面對如此龐大的訪問量和數據量,單臺服務器已經不可能適應了,而 ACID 在集羣環境,幾乎不可能達到咱們的預期,保證了 ACID,效率就會大幅度降低,更要命的是,這麼高的要求,很差擴展~因而又了 CAP 原則(Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性))和 BASE 原則(Basically Available(基本可用)、Soft state(軟狀態)、Eventually consistent(最終一致)),看看它們的英文,Availability/Basically Available,Consistency/Eventually consistent,基本上,BASE 原則對 CAP 原則的進一步詮釋。數據庫

本文是Ebay的架構師在2008年發表給ACM的文章,是一篇解釋BASE原則,或者說最終一致性的經典文章. 文中Dan討論了BASE與ACID原則的基本差別, 以及如何設計大型網站以知足不斷增加的可伸縮性需求,期間如何對業務作調整與折衷. 以及一些具體的折衷技術的介紹.後端

在對數據庫進行分區後,爲了可用性(Availability)犧牲部分一致性(Consistency)能夠顯著的提高系統的可伸縮性(Scalability).緩存

——By DAN PRITCHETT, EBAY ,Translated by Jametong安全

Web應用在過去10年變得愈來愈普及.不管是爲最終用戶仍是爲應用開發者構建的應用,對這個應用的但願極可能都是,此應用被最普遍的用戶使用-普遍的使用會帶來交易的增加.業務若是依賴於持久化,數據存儲就極可能成爲瓶頸.服務器

擴展任何應用都有兩種策略.第一種,也是最簡單的一種,就是縱向擴展:將應用遷移到更大更強的計算機上. 目前可用的最大的機器也知足不了它的容量是它最明顯的限制.縱向擴展也很昂貴,增長交易容量一般都須要購買下一個更大的機器.縱向擴展一般還會產生對供應商的依賴,從而進一步增長成本.網絡

橫向擴展(Horizontal Scaling)提供了更多的靈活性,但也會顯著的增長複雜度.橫向數據擴展可能沿着兩個方向發展.按功能擴展(Functional Scaling)牽涉到按功能對數據進行分組,並將不一樣的功能組分佈在多個不一樣的數據庫上.在功能內部將數據拆分到多個數據庫上,也就是進行分片 (Sharding),它爲橫向擴展增長一個新的維度.圖-1簡要闡釋了橫向數據擴展策略.架構

fig1

圖 1app

如圖-1所示,橫向擴展的兩種方法能夠同時進行運用.用戶信息(Users)、產品信息(Products)與交易信息 (Transactions)能夠存儲在不一樣的數據庫中.另外,每一個功能區域根據其交易容量(transactional capacity)能夠再拆分到多個數據庫中.如圖所示,功能區域能夠相互獨立地進行擴展.

功能分區(Functional Partitioning)

功能分區對於實現高可伸縮性至關重要.每一種好的數據庫架構都會根據功能將概要(Schema)分解到多張表中.用戶(Users)、產品 (Products)、交易(Transactions)以及通信都是功能分區的例子. 經常使用的方法是,利用諸如外鍵(foreign key)一類的數據庫概念來維持這些功能區域之間的數據一致性.

依賴數據庫的約束保證功能組之間的一致性,會致使數據庫的不一樣概要(schema)在部署策略上高度耦合.要支持約束,表必須存在單一的數據庫服務器上,當交易率(transaction rate)增加時也沒法對其進行橫向擴展.不少狀況下, 將數據的不一樣功能組遷移到相互獨立的數據庫服務器上是最容易實現的向外擴展(Scale-out)方案.

可擴展到很是高的交易量的概要會將不一樣的功能的數據放置在不一樣的數據庫服務器上.這須要將數據之間的約束從數據庫遷移到應用中去. 同時這也將引入一些新的挑戰,本文的後續內容會對此進行深刻探討.

CAP定理(CAP Theorem)

Eric Brewer,一位加州大學伯克利分校的教授,Inktomi公司的共同創辦人以及首席科學家,做出瞭如下推測,Web服務沒法同時知足如下3個屬性(由其首字母構成縮寫CAP):

  • 一致性(Consistency).客戶端知道一系列的操做都會同時發生(生效).
  • 可用性(Availability).每一個操做都必須以可預期的響應結束.
  • 分區容錯性(Partition tolerance).即便出現單個組件沒法可用,操做依然能夠完成.

具體地講,在任何數據庫設計中,一個Web應用至多隻能同時支持上面的兩個屬性.顯然,任何橫向擴展策略都要依賴於數據分區;所以,設計人員必須在一致性與可用性之間作出選擇.

ACID解決方案

ACID數據庫事務極大地簡化了應用開發人員的工做.正如其縮寫標識所示,ACID事務提供如下幾種保證:

  • 原子性(Atomicity).事務中的全部操做,要麼所有成功,要麼所有不作.
  • 一致性(Consistency).在事務開始與結束時,數據庫處於一致狀態.
  • 隔離性(Isolation). 事務如同只有這一個操做在被數據庫所執行同樣.
  • 持久性(Durability). 在事務結束時,此操做將不可逆轉.(也就是隻要事務提交,系統將保證數據不會丟失,即便出現系統Crash,譯者補充).

數據庫廠商在好久之前就認識到數據庫分區的必要性,並引入了一種稱爲2PC(兩階段提交)的技術來提供跨越多個數據庫實例的ACID保證.這個協議分爲如下兩個階段:

  • 第一階段,事務協調器要求每一個涉及到事務的數據庫預提交(precommit)此操做,並反映是否能夠提交.
  • 第二階段,事務協調器要求每一個數據庫提交數據.

若是有任何一個數據庫否決這次提交,那麼全部數據庫都會被要求回滾它們在此事務中的那部分信息.這樣作的缺陷是什麼呢? 咱們能夠在分區之間得到一致性.若是Brewer的猜想是對的,那麼咱們必定會影響到可用性,但,怎麼能夠這樣呢?

任何系統的可用性都是執行操做的相關組件的可用性的產物.此陳述的後半段尤爲重要.系統中可能會使用但又不是必需的組件,不會下降系統的可用性.在兩階段提交中涉及到兩個數據庫的事務,它的可用性是這兩個數據庫中每個的可用性的產物.例如,若是咱們假設每一個數據庫都有爲99.9%的可用性,那麼這個事務的可用性就是99.8%,或者說每個月43分鐘的額外停機時間.

關於兩階段提交,你能夠看看「改變將來的九大算法」,裏邊有精闢的講解~

一種ACID的替代方案

若是ACID爲分區的數據庫提供一致性的選擇,那麼你如何實現可用性呢?答案是BASE(基本上可用、軟(弱)狀態、最終一致性).
BASE與ACID截然相反.ACID比較悲觀,在每一個操做結束時都強制保持一致性,而BASE比較樂觀,接受數據庫的一致性處於一種動盪不定的狀態.雖然,聽起來很難應付,實際上這至關好管理,而且可帶來ACID沒法企及的更高級別的可伸縮性.

BASE的可用性是經過支持局部故障而不是系統全局故障來實現的.下面是一個簡單的例子:若是用戶分區在5個數據庫服務器上,BASE設計鼓勵相似的處理方式,這樣一個用戶數據庫的故障只會影響這臺特定主機上的那20%的用戶.這裏不涉及任何魔法,不過,它確實能夠帶來更高的可感知的系統可用性.

所以,到目前爲止,你已經將數據分解到了多個功能組中,並將最繁忙的功能組分區到了多個數據庫中,如何在你的應用中應用BASE原則呢?與ACID 的典型應用場景相比,BASE須要對邏輯事務中的操做進行更加深刻的分析.到底該如何進行分析呢?後續的內容將提供部分指導原則.

一致性模式(Consistency Patterns)

沿着Brewer的猜想,若是BASE在分區數據庫中選擇保留可用性(Availability), 那麼,弱化必定程度的一致性就成爲必然的選擇.這一般難以決策,由於商業投資方與開發人員都傾向於認爲一致性(Consistency)對應用的成功相當重要.哪怕是臨時的不一致也瞞不過最終用戶,所以,技術部門與產品部門都須要參與進來,以決定將一致性弱化到什麼程度.

圖-2是一個簡單的概要,它闡釋了BASE中一致性要考慮的事情.用戶表存儲用戶信息,同時還包含總銷售額與總購買額.這些都是運行時的統計.交易表存儲每一筆交易,將買家、賣家以及交易金額關聯在一塊兒.這些是對實際使用的表進行過分簡化後的結果,不過,它已經包含闡釋一致性的多個方面的必要元素.

fig2

圖 2

通常來講,功能組之間的一致性要比功能組內部的一致性要更加容易弱化.這個示例概要包含兩個功能組:用戶與交易.每當售出一個條目(的商品),交易表中就會增長一條記錄,買家與賣家的計數器都會被更新.使用ACID風格的事務,SQL語句可能如圖-3所示.

fig3

圖 3

用戶表中的總銷售額的列與總購買額的列能夠被認爲是交易表的一份緩存(Cache).它的存在是爲了提升系統的效率.有鑑於此,一致性的約束能夠被弱化. 能夠調整一下買家與賣家的指望設置,從而他們的運行結餘(running balance)不能當即反映交易的結果.這種狀況很常見,實際上,人們常常會遇到交易與運行結餘之間的這種延遲(例如,ATM取款或者手機通話).

如何修改SQL語句來弱化一致性要取決於如何定義運行結餘,若是它們只是簡單的估計,也就是部分交易能夠被錯過不統計,SQL的修改很是簡單,如圖-4所示.

fig4

圖 4

如今,咱們已經將對用戶表與交易表的更新作了解耦.兩個表之間的一致性將再也沒法保證.實際上,在第一個事務與第二個事務處理間隔發生故障,將致使用戶表持久處於不一致的狀態,不過,若是合同約定運行時彙總(running total)是估計值的話,這樣作也足夠了.

若是沒法接受估計值,該怎麼辦呢?如何繼續對用戶表與交易表的更新進行解耦呢?引入一個持久消息隊列來解決此問題. 有多種選擇能夠實現持久消息.然而,實現此消息隊列的最關鍵的因素是,確保隊列的持久化支持與數據庫使用一樣的資源.要實現隊列在不涉及2PC的狀況下按事務提交,這樣作頗有必要 .如今的SQL操做看上看去有點不一樣了,如圖-5所示.

fig5

圖 5

這個例子中的語法有點隨意,爲了闡釋概念對其邏輯也作了大量的簡化.經過在插入語句的同一個事務中對持久消息進行排隊,能夠抓取更新用戶運行結餘所需的信息.這個事務包含在同一個數據庫實例中,所以,它不會影響系統的可用性.

一個獨立的消息處理組件,會從隊列中取出每條消息,並將此信息應用到用戶表.這個例子看似解決了全部的問題,可是,還有一個問題沒有解決.爲了不排隊時發生2PC,消息是持久化在交易的主機上的.若是在涉及到用戶主機的事務中從隊列中取出消息,咱們仍將遇到2PC的情景.

消息處理組件中的2PC的一種解決方案是什麼都不作.經過將更新操做解耦到一個獨立的後端(back-end)組件,能夠保持面向客戶的組件的可用性.業務須要或許能夠接受較低的消息處理器的可用性.

不過,假定你的系統徹底沒法接受2PC.這個問題該如何解決呢?首先,你須要理解等冪概念.若是一個操做被應用一次或屢次都能取得一樣的結果,就被認爲是等冪的.等冪操做很是有用,由於它們容許局部故障,重複執行它們不會改變系統的最終狀態.

從等冪的角度看,所選的這個例子是有問題的.更新操做一般不等冪.這個例子中有累加帳戶列的操做.重複應用此操做顯然會致使錯誤的帳戶餘額.然而, 即便是僅僅設定一個值的更新操做也不是等冪的,由於它還涉及到操做執行的順序.若是系統沒法保證更新操做按照接收到的順序被應用,系統的最終狀態也將是不正確的.後面的內容會進一步討論此問題.

在帳戶更新的例子中,你須要一種方式來跟蹤哪些更新已經應用成功,哪些更新仍然未解決.一種技術是,使用一個表來記錄已經應用的那些交易的惟一識別號.

圖-6中展現的表會記錄交易ID、更新了哪一個賬號以及應用此賬號的用戶ID.如今,咱們的樣本僞代碼如圖-7所示.

fig6

圖 6

fig7

圖 7

這個例子取決於能夠窺視隊列中的一條消息,並在成功處理後當即刪除此消息.若有必要,能夠經過兩個獨立的事務來處理它:消息隊列上一個事務,用戶數據庫上一個事務.數據庫操做成功提交,才提交隊列操做.目前的算法能夠支持局部故障,並且又能提供不依賴於2PC的事務保證.

若是隻是關注更新的順序的話,還有一個更加簡單的技術能夠確保等冪更新.咱們來稍微調整一下咱們的示例概要,來闡釋面臨的挑戰以及相應的解決方案(見圖-8).

fig8

圖 8

假設兩筆購買交易在一個很短的時間窗口內發生,咱們的消息系統沒法確保順序操做.您如今面臨的狀況是,取決於消息被處理的順序,last_purchase可能出現一個不正確的值.幸運的是,能夠經過對SQL語句作點簡單調整來解決此類更新問題, 如圖-9所描述.

fig9

圖 9

僅僅經過不容許last_purchase時間作逆向調整,就能夠作到更新操做順序不相關.也能夠經過這種方法來保護任何更新免遭無序更新(out-of-order update).你還能夠嘗試使用單調遞增的事務ID來取代時間.

消息隊列的順序

關於順序消息投遞,下面這個簡短地附屬說明可能有用.消息系統能夠提供確保消息發送的順序與接收的順序一致的能力.不過,支持此功能可能很是昂貴,一般也沒有必要,實際上,有時它也只是給出了一種虛假的安全感.

這裏提供的例子闡釋瞭如何弱化消息的順序,並在最終仍然可以提供一個數據庫的一致性視圖.弱化消息排序所需的開銷是名義上的,在大部分狀況下,此開銷要顯著的少於在消息系統中確保消息順序的開銷.

進一步講,不管互動風格如何,Web應用在語義上都是一個事件驅動的系統.客戶端請求以任意順序達到系統.每一個請求所需的處理時間要求也各不相同. 整個系統的不一樣組件的請求調度也是不肯定的,致使了消息排隊的不肯定.要求保持消息的順序給出的是一種虛假的安全感.簡單的事實是,不肯定的輸入會致使不肯定的輸出.

弱狀態/最終一致性(Soft State/Eventually Consistent)

到此爲止,重點一直是爲了可用性而權衡犧牲部分一致性.硬幣的另一面是,理解軟狀態與最終一致性對應用設計有何影響.
因爲軟件工程師傾向於認爲系統是閉環(closed loop)的.從預見投入產生預見的產出方面講,咱們能夠這樣考慮他們行爲的可預測性.這對於建立正確的軟件系統很是必要.好的消息是,在大部分狀況下使用BASE不會改變一個閉環系統的可預測性,不過,它確實須要從總體上來進行審視.

一個簡單的例子就能夠幫助解釋這一點.考慮這樣一個系統,用戶能夠在此將資產轉移給另外一個用戶.哪一種類型的資產都沒有關係,它能夠是錢或者遊戲中的裝備.對於這個例子,咱們假設,已經經過使用一個用於解耦的消息隊列,對以下兩個操做進行了解耦:從一個用戶取出資產,將資產給另外一個用戶.

很快,系統就會感受到有問題與不肯定性.在資產離開一個用戶到達另外一個用戶中間,有一段時間的延時. 這個時間窗口的大小由消息系統的設計所決定.不管如何,在開始狀態與結束狀態之間,始終會有一個時間間隔,在這段時間內, 看似任何用戶都不享有這筆資產.

不過,若是咱們從用戶的視角來考慮這個問題,這個時間間隔可能就是無所謂的或者根本就不存在.不管是接收的用戶仍是發出的用戶可能都不知道資產將在什麼時候到達.若是在發送與接收之間的時間間隔是幾秒鐘,對於具體溝通資產轉移的用戶來說,它將是隱蔽的或確實能夠忍受的.在這種情況下,這種系統行爲對用戶來說,就是一致並可接受的,即便,咱們在實現中依賴了軟狀態以及最終一致性.

事件驅動架構(Event-Driven Architecture)

若是你確實須要知道,系統將在什麼時候達到一致的狀態?你可能須要一種算法,來應用到這個狀態上,不過,僅僅在它達到一個與後續請求相關的一致狀態時纔會被應用.

繼續討論前面的例子,若是在資產到達時,須要通知用戶,怎麼辦? 在將資產交付給接收用戶的那個事務內建立一個事件,就能夠提供一種機制,當達到一個事先肯定的狀態時,能夠作進一步的處理.EDA(事件驅動架構,Event-Driven Architecture)能夠顯著改善可伸縮性以及架構的解耦.對於EDA應用的進一步討論超出了本文的範疇.

結論

顯著的擴展系統的交易率,須要以一種全新的方式來考慮如何對資源進行管理.當負載須要分佈到大量的組件上時,傳統的事務模型會漏洞百出.對操做進行解耦,並依次對它們進行處理,可能提供更好的可用性與伸縮性,不過是以犧牲一致性爲代價.BASE提供了一種模型來考慮這種解耦.

參考

Dan Pritchett是Ebay的一位技術人員,他過去4年一直是Ebay架構團隊的成員.在此崗位上,他與Ebay市場部、Paypal以及Skype的戰略、商業、產品與技術團隊進行合做.他已經有20年技術公司的工做經歷,他服務過的公司包含Sun,HP以及硅谷圖形公司(Silicon Graphics),Pritchett具備豐富的技術經歷,從網絡層協議與操做系統到系統設計與軟件模式.他擁有密蘇里州Rolla大學的計算機科學學士學位.

相關文章
相關標籤/搜索