探索Cassandra的去中心化分散式架構

語言: CN / TW / HK

關係型模型之父Edgar F. Codd,在1970年Communications of ACM 上發表了《大型共享資料庫資料的關係模型》,成為了永恆的經典,關係模型的語義設計易於理解,語法上巢狀、閉環、完整,因此在資料庫領域,關係模型普及與流行了數年之久。

在此之後,IT世界湧現了很多非常著名的RDBMS(關係型資料庫系統),包括了Oracle、MySQL、SQLServer、DB2、PostgreSQL等。

1. 傳統關係型資料庫的分散式瓶頸

但是RDBMS的技術發展由於架構上的限制,遇到了很多問題,例如:關係模型的約束必須在設計前明確定義好屬性,這就難以像很多NoSQL一樣,具有靈活可變的模式,很難適應敏捷迭代的需要。

其實最難解決的一個問題,就是資料表的分散式化。

為什麼會導致如此現象呢?

本質上,由於關係模型在前期設計上的強關聯,導致表表之間的連線關係變得極為緊密。

例如:有一種比較普遍的業務場景中連線操作,通過A->B->C的關聯查詢,然而這種關聯所形成的鏈,就將A、B、C緊密地捆綁在了一起。

那麼這種緊密關係帶來了什麼問題呢?

那就是RDBMS在設計之初,很難去考慮到資料表在分散式網路環境中的通訊解耦設計,而只是單純的資料表本地檔案的IO掃描,而且在過去低頻寬的網路環境中,模型關係的分散式化,更是難以想象。

如下圖所示:

我們從上圖可以看到,教師、學生和課程這三張表具有多對多的緊密關係(其中還有一些未展示的中間表),那麼這種緊密的關係投射到具體的資料庫物理檔案上,就是圖中多個物理上的表文件(Table Data),當我們查詢時,就是通過關係邏輯對這些表文件進行了頻繁的IO掃描。

在這種模式下,我們會發現一個問題,這些表實際上很難跨資料庫而分散式拆分,也就是說,RDBMS很難以分散式形式,把教師表放到伺服器1的資料庫1上,學生表放到伺服器2的資料庫2上,課程表放到伺服器3的資料庫3上。

而且更難做到的是:將1萬名學生總共10萬次課程活動所產生的學生上課資料分拆成10個1萬條資料表,再將表分佈在不同的資料庫中。

如下圖所示:

我們提出一個假設:如果能將學生課程活動表拆分到分散式網路中多個數據庫例項當中,那麼就極大地降低了單臺數據庫的負載。這對於訪問量和資料量已經規模化的應用系統來說至關重要。

然而對於RDBMS來說,這種假設的目標實現是一件非常痛苦且困難的事情,例如:我們可以在網上搜索到大量關於MySQL分庫分表的文章,這類文章的主題都是在對RDBMS進行分散式化的分割槽操作。

但是這種操作並非資料庫天然所支援,必須組建專業的資料工程師團隊,耗費大量的時間對資料庫進行精密地調節,必須自己去解決分散式叢集的諸多問題,例如:容錯、複製、分割槽、一致性、分散式事務等等,其難度可想而知,所以這不是一般技術企業所能承擔的巨大維護成本。

隨著網際網路時代的發展,網際網路應用系統面向高併發、海量資料的規模化趨勢愈演愈烈,RDBMS對於資料表進行分散式化的瓶頸也越來越明顯,這也就促成了NoSQL在網際網路等領域快速發展,接下來我們重點看看NoSQL的解決方案。

2. 中心化分散式架構的弊端

2.1 Hadoop分散式檔案系統(HDFS)

談到大資料領域的分散式資料庫體系,必然繞不開Hadoop,Hadoop實際上是一個系統生態,基於Hadoop生態的各種分散式資料庫都依賴於一個數據底座——HDFS,其實最早Google發明了GFS分散式檔案系統之後,進行了論文發表,然後在開源界才產生了Hadoop分散式檔案系統(HDFS)。

如下圖所示:GFS/HDFS的特點表現在順序的、成塊的、無索引的向檔案塊中寫入資料,並在叢集環境中按塊(block)均勻分佈儲存,需要使用時,再通過MapReduce、Spark這些批處理引擎發起並行任務,按塊,批次地讀取分析。這樣就把寫入和並行讀取的效能發揮到了極致,具備了任何建立索引的資料庫都無法比擬的讀寫速度。

HDFS在對多個數據節點(DataNode)協調寫入資料的過程中,一定是由一箇中心化的服務進行了全域性排程,那就是NameNode節點。

也就是說NameNode節點會是一個單點風險,若出現了故障,整個HDFS分散式檔案系統叢集就會出現崩潰。

因此HDFS為了NameNode的高可用(HA),實現了極為複雜的NameNode HA架構。

但是,在同一時刻,只可能有一個NameNode為所有資料的寫入提供排程支撐,也就是說從併發負載的角度,NameNode始終會是一個瓶頸。

由於HDFS是整個Hadoop生態的資料底座,那麼Hadoop的分散式架構就始終圍繞在分散式中心化的路線上前進,然而中心化架構最大的問題就在於無論上層建築如何變化,傳導到資料底座後,HDFS作為中心管理者,一定會在高併發、大規模訪問情況下成為瓶頸。

另外從叢集節點的伸縮擴充套件方面也有隱患,問題來自於元資料在NameNode記憶體中可能會出現溢位。

2.2 大規模結構化的分散式資料庫(HBase)

然而HDFS是面向成塊的大檔案資料,無索引地追加讀寫,也就不具有資料隨機查詢能力,另外缺少結構化設計機制,像集合的資料項掃描、統計和分析就無法獨立支撐,必須構建上層資料庫系統合作來完成,因此就產生了鼎鼎大名的HBase。

如下圖所示:我們可以從圖中看到HBase的資料底座完全依賴HDFS,也就是說資料如何物理上分佈是由HDFS所決定。

HBase實現了全域性排序的K-V模型,滿足海量資料的存放條件下通過行鍵定位結果,能達到毫秒級響應的資料庫,或者通過主鍵排序掃描實現了統計分組。

儘管HBase對於大規模結構化資料的寫入、排序掃描和聚合分析有著巨大的效能優勢,但是HBase的查詢設計目標並不是解決二次索引的大範圍查詢。

我們再看HBase完成資料切分的辦法——Region切分,當Region(我們可以理解為資料表)追加資料超出閥值後,就要進行Region拆分,然後拆分出的新Region分佈到別的RegionServer中。

這裡有兩個問題:

1)同一時刻向Region寫入新資料的伺服器必須是唯一的,那麼這裡就會出現RegionServer的高併發訪問瓶頸。

2)由於分散式架構設計上的約束,使得HBase不具有二級索引,在隨機查詢過程中,只能根據全域性的Region排序進行掃描,這就無法承載Web應用的資料隨機查詢的實時性。

因此HBase一般會作為大規模資料流形式匯入的OLAP系統。

3. 去中心化分散式架構解析

那麼到底有沒有一種面向海量的結構化資料儲存,可以實現大規模Web應用支撐的分散式資料庫架構呢?

既能突破RDBMS的資料表在分散式過程中的瓶頸,又能解決Hadoop/HBase在高可用性問題上的隱患,同時還能滿足高併發、大規模、大範圍的隨機查詢要求。

答案是有的!

就是那篇改變網際網路發展程序的論文《Dynamo: Amazon's Highly Available Key-value Store》,這篇論文源自於Amazon,對於自家資料庫的架構設計的經驗總結。

鼎鼎大名的分散式開源資料庫Cassandra在分散式設計方面也完全繼承了這篇論文的設計思想,只不過在資料模型方面又借鑑了Google BigTable的資料模型。

Cassandra分散式核心思想就是去中心化,形成了分散式系統世界的獨特一面。

我們重點就是去解析這種去中心化的分散式架構的一些核心技術,到底有多麼厲害,為什麼可以解決我們前面所述的三個問題:

  • 支撐大規模的結構化資料
  • 解決資料表的分散式讀寫存放
  • 滿足高併發、大規模、大範圍的隨機查詢。

3.1 什麼是去中心化?

去中心化不同於中心化的核心特質在於任一服務例項在叢集網路中都是以級別對等、點對點的形式存在,這就不存在服務節點在叢集中的等級劃分,也就是說,任何一個Cassandra服務節點,從維護者的角度都是一樣的角色,那麼這就極大降低了維護者運維的複雜性。

例如:我們對於Cassandra資料庫的所有節點都可以稱之為資料節點,叢集中承擔了相同的角色。

但是HBase/HDFS就不一樣了,HDFS叢集分為:NameNode、DataNode、JournalNode、ZKFC、Zookeeper等承擔了叢集的不同角色;HBase叢集又分為:HMaster、HRegionServer、Zookeeper等承擔了叢集的不同角色。

從維護者的角度,肯定是叢集角色統一的情況下更易於維護。

去中心化的另外一個特點就是高可用性非常好,伸縮性也很好,叢集擴充套件幾乎是無限制的。

對於沒有一箇中心節點進行排程的情況下,又能保證如此優異的高可用性、伸縮性,那麼它是怎麼做到的呢?

從原理上Cassandra主要表現為四個方面的特徵:

  • 利用一致性雜湊環機制實現資料的分割槽分佈和擴容縮容的資料遷移。
  • 利用gossip協議在對等節點的網路傳播下保持叢集狀態一致性。
  • 利用anti-entropy(反熵)機制實現資料讀取過程中節點之間的比對,保證資料一致性。
  • 基於hinted handoff機制,按照最終一致性的模式,可以極大提升叢集可用性。

以上這些特徵都是叢集中網路節點在對等條件下,基於共識機制,而非管理排程所形成的狀態協同。

在這種機制之下,叢集就具有的非常優異的高可用性以及伸縮性,也就是說,對於整個叢集來講,無論是擴容增加了很多網路節點,還是突然故障減少了很多網路節點,網路節點間的關係都是弱關聯,也就難以對其它節點形成健康影響,對於使用者幾乎是無感知的。

從去中心化的特點,我們再對比一下HBase以及所依賴的Hadoop HDFS,這種基於中心化的集中式排程管理,HBase就存在HMaster的叢集單點故障風險,因此一般HBase的HMaster可以有一個或多個HA熱備,儘管引入HA後的HBase叢集依然很健壯,只是必然引入更高的部署複雜度,底層依賴的HDFS NameNode HA在服務部署複雜性方面則更甚之。

而增加分散式系統的複雜性只會帶來更復雜和不確定的運維問題。

3.2 分割槽機制

Cassandra在實現去中心化架構的過程中,關鍵應用目標就是在結構化資料的大規模寫入能力的基礎之上,還能支撐大範圍的海量資料的隨機查詢,這點就完全區別於HBase了,事實上HBase是對於OLAP業務場景的支撐上做到了一種分散式極致的表現。

但是Cassandra則完全進入到了另一種狀態,那就是對於大規模的OLTP業務實現了強力支撐,可以在毫秒級、秒級、亞秒級的範圍承諾讀、寫以及分散式事務的SLA(服務級別協議)。

一致性Hash環

如下圖所示:Cassandra的分佈是基於一致性雜湊所形成的環狀結構。

每個節點就好像分佈在一個環形上。每個節點都是對等的,在讀寫過程中,每個節點面對客戶端都是協調節點,並與其他所有節點直接形成單跳,這也是Amazon Dynamo論文中關鍵的分散式架構設計。

編輯

從上圖中我們可以看到12個節點分佈在Data Center1,Data Center2兩個資料中心的四個機架(Rack)上,並通過DynamoDB叢集的一致性雜湊環聯絡在了一個分散式資料庫中。

上圖是資料寫入過程的示例,首個副本定位在節點1(DC1,Rack1)上,然後第二個副本就會繼續順著環尋找,並定位在節點2(DC2,Rack1)上,最後第三個副本就需要在DC1中優先尋找到Rack2的節點位置,正好定位在節點3(DC1,Rack2)上。 通過環順時針尋找定位,三個副本在DC1、DC2中分別存放,在DC1的Rack1、Rack2上交替存放。

通過使用這種跨資料中心(DC)的副本配置機制,使叢集具有了更強的容災能力,對大型應用平臺的支撐也更具均衡負載和高可用優勢;同一個DC的資料副本會在不同Rack節點上交替存放,以便鞏固資料在叢集分佈的安全性。

那麼進行了這種Hash分割槽之後,從均衡負載的角度將又帶來哪些優勢呢?我們接著看。

均衡負載案例

如下圖所示:這是一個網際網路醫療平臺的應用場景。

我們應用了Cassandra,對錶進行了複合主鍵設計,Group為Hash分割槽鍵,id為排序鍵。

由於我們在Cassandra中儲存了很多家醫院的大量醫生與患者之間的診療資料,傳統RDBMS診療表會存在單點寫入,無法進行均衡負載的瓶頸。

但是Cassandra的這種Hash分割槽則不同,我們可以將診療資料分割槽到3臺不同的資料節點上進行讀寫,其中設定訪問量最大的1家三甲醫院的Hash分割槽鍵為Group1,5家市級醫院的訪問量足以對標省級三甲醫院,將此5家組成Hash分割槽鍵Group2,其他50家縣級醫院的訪問量也形成了足夠的訪問量,組成Hash分割槽鍵Group3。

那麼通過Hash分割槽形式,就可以對所有診療資料按照醫院分組的形式進行了資料切分,均衡負載到了3臺伺服器的Cassandra資料節點當中。

其次我們使用診療號(id)作為排序鍵,只要是同一分割槽的診療資料,無論是掃描哪一家醫院的診療資料,都可以根據id進行排序掃描,基於排序資料也更易於統計分析。

故障轉移

如下圖所示:在一致性雜湊環中我們設定了4個節點,那麼雜湊範圍就被定義為:Node1-Node2,Node2-Node3,Node3-Node4,Node4-Node1。

只要KV資料的主鍵Hash值落在某個範圍內,就會順時針找到範圍尾部的節點落地。可是問題來了,節點2因為故障下線,通過備份機制我們可以將節點2的資料還原回來,但根據一致性雜湊演算法,只能轉移到節點3上,那麼節點3就承載了整個叢集50%的資料量,這顯然就出現資料分佈的嚴重傾斜了。

因此Cassandra在一致性Hash的基礎上又增加了虛擬節點,例如:Cassandra的預設分割槽策略就會建立1024個虛擬節點(Token),將-2^63~2^63-1的範圍值進行平均切分,這1024個Token又被平均分配給這4個節點,這就形成了所有節點在一致性雜湊環中交替且均勻出現的現象,這樣如果再出現上述圖中的故障,故障節點2上的副本資料就不會在恢復的時候,全壓在節點3上,而是在節點1、3、4上平均地消化掉。

總之

Cassandra使用分割槽鍵進行Hash分割槽實現了資料在不同資料節點中的均衡負載,又通過改進型的一致性Hash演算法與結構,使得資料節點無論是擴容還是縮容,都使得叢集整體資料的變化遷移很小,而且還優雅地形成了多資料中心的異地容災機制。

我們在看待Cassandra的核心優勢上,就在於去中心化的這種對等節點所形成的大規模資料訪問的均衡性,以及伸縮性的低成本維護特性,其實本質上,它們都是非常有利於雲廠商形成無服務化的分散式資料庫服務。

3.3 一致性

Amazon Dynamo論文的初衷是建立一個最終一致性的分散式資料庫系統,但是同樣也支援分散式的強一致性。

同樣,對於Cassandra的應用,我不建議將強一致性作為主要應用考慮層面,因為強一致性對於網路頻寬,客戶端請求延時都有較大的資源消耗以及不可預估的問題。

那麼什麼是分散式的一致性呢?

CAP定理

我們在研究和應用分散式架構的過程中,對於CAP理論是必須作為基礎概念去強化理解的。

CAP:一致性(Consistency),可用性(Availability)和分割槽容錯性(Partition Tolerance)。

我可以這樣去理解CAP:對於集群系統,網路分割槽無法訪問的現象在將來是必然會發生的,當發生故障的時間裡,正好要進行著一個分散式業務,例如多副本複製,就一定會出現不同節點的資料不一致。

那麼我們為了保證分割槽容錯性,我們只能有兩種選擇:

第一種情況就是等待故障的節點分割槽恢復,否則就不給客戶端反饋,證明我們保證了強一致性。

第二種情況就是不等故障節點分割槽恢復了,先返回客戶端當前已經寫入完成的訊息,等故障節點恢復後,再自行恢復資料,證明我們保證了高可用性。

因此當分割槽需要容錯的情況下,我們只有CP或者AP兩種選項。

對於高可用性系統,我們重點保證AP;對於強一致性系統,我們重點保證CP(HBase就是強一致性設計)。

一致性方法對比

我們前面說了,Cassandra主要滿足最終一致性,也就是說當資料寫完一個節點或複製不超過叢集副本數量一半,寫入成功的結果就可以反饋客戶端,因此,複製未全部完成的期間,很可能其他節點或另一半的節點還在為另一個客戶端的讀取提供過期的資料。

其實Cassandra還有一種叫QUORUM級別的一致性,只有超過一半的節點寫入成功後才反饋客戶端成功,同樣讀取的時候也必須超過一半的節點一致才返回讀取結果,那麼讀寫一旦產生交集,客戶端始終讀取到的資料是寫入成功的資料,其實這種模式是在最終一致性和強一致性之間最佳的一種平衡。

如下圖所示:

上圖第一種情況就是Cassandra的QUORUM級別,讀取副本時,必須讀取3個節點,如果3個節點是不一致的,它必須等待最後一箇舊的節點被複製成功,3個節點才會一致,才能返回結果。

上圖第二種情況就是典型的最終一致性了,這是個寫入不到一半節點就返回成功的案例,當第一個客戶端寫入節點成功返回後,在沒有複製到另一半節點的情況下,第二個客戶端可能讀取到資料依然是兩外2個節點的過期資料。

總之

最終一致性必然帶來了更少的網路資源消耗,也降低了網路分割槽出錯的不可預估性,提升了叢集的高可用性。但是可能會讀取過期的資料。

那麼什麼樣的應用場景會適合最終一致性呢?其實最常見的一個場景就是:購物車!

購物車可能在很小的機率情況下,讀取到自己過期的資料,但是並不影響使用者的最終使用體驗,因為購物車並不是電商流程的終點,但一定是最頻繁操作的業務之一,而最終只有訂單確定了才算數。

同樣像:社交網路、遊戲、自媒體、物聯網都是這種情況,這種規模化的聯機資料業務,並不一定非得要保證讀寫的強一致性,一旦出現讀寫不一致,也許再訪問一次,資料就是正確了,但是叢集保證了在大規模訪問場景下快速地響應客戶,在這一點上,具有RDBMS無法比擬的天然優勢。

4. 最後

分散式去中心化架構思想不僅應用於海量規模的結構化資料的分散式資料庫系統,其實在其他領域也有應用。

例如:作為記憶體字典的Redis也具有Redis Cluster這種去中心化的分散式架構,Redis Cluster的主要價值也是為了更好的負載高併發的業務,例如:秒殺、搶單,完全可以先在Redis Cluster中完成,再同步到RDBMS當中。

但是無論是Redis Cluster,還是Cassandra,作為去中心化的分散式架構,都離不開為了維護叢集節點對等的狀態一致性,使用了類似病毒傳播的Gossip協議,從一定程度,會給叢集網路帶來比較大的狀態傳播壓力,另外基於共識的協議,如果叢集網路節點出現步調不一致故障,對於運維人員的排查判斷也會造成諸多不便。

作為雲技術廠商對於去中心化的叢集狀態維護能力是遠遠大於普通企業團隊或個人的,因此對於去中心化的叢集架構,我更建議兩種應用場景:

1)對於普通小團隊的小型專案,進行小規模叢集應用,可以考慮部署Cassandra叢集,維護非常方便,也能快速支撐起一些海量資料查詢業務,例如:很多政府型別與科研型別的專案。

2)對於具有一定資料規模量,且訪問併發量較大的中大型的網際網路專案,可以考慮直接上雲,使用雲廠商提供的Cassandra服務,因為叢集中的節點數量較多的情況下,故障率會同步提升,但自主運維並不是一件容易的事情。

無論你會怎麼選,作為分散式去中心化架構下的資料庫,為我們在網際網路應用業務的規模化發展支撐上,提供了一種很不錯的選擇。


本文原創,轉載文章,務必聯絡作者!