Cassandra 卓越點 寫操作 單點故障 讀操作緩慢

 http://hi.baidu.com/yandavid/blog/item/f5f72d9bcc93c1a4c8eaf41a.html

 

Cassandra的寫性能表現卓越這一點衆所周知,Cassandra最好運行在多臺機器上.據知,Twitter使用了一個45臺機器組成的集羣.在一臺機器上運行Cassandra可能不是很有意義,因爲你將失去沒有單點故障的系統的優勢.

客戶端向一個隨機的Cassandra節點發出一個寫請求.這個節點作爲代理往集羣寫入數據.節點的集羣存儲在一個節點”環”上,寫會按照複製放置策略(replication placement strategy)複製到N個節點上.當使用RackAwareStrategy策略時,爲了保證可靠性(reliability)與可用性(Availability), Cassandra會按照複製節點到當前節點的距離將複製節點分爲3個桶:與當前節點位於同一機架、與當前節點位於同一數據中心、或位於不同的數據中心.你配置了Cassandra寫數據到N個節點來做冗餘,Cassandra會將第一份拷貝寫入到此數據的主節點,第二份拷貝到環上的位於另一個數據中心的節點,剩餘的其它拷貝到與代理節點位於同一個數據中心的機器上.這樣就可以確保單點故障不會導致整個集羣不可用,即使在整個數據中心都不可用時集羣仍然保持可用.

因此,寫請求從你的客戶端出發到單一隨機節點,此節點根據複製放置策略將寫操作發送到N個不同的節點.我沒有在此討論很多邊緣用例(節點宕機、集羣中新增節點、等等),但是,節點需要等待N個節點返回成功並返回成功給客戶端.(此處的描述有問題,Cassandra中,還有另外一個W的參數,也就是需要等待幾份寫拷貝成功才返回成功給客戶端,譯者加).

節點中的每一個都會以“RowMutation“消息的形式接收到此寫請求.對於此消息,節點會採取以下兩種行動:
追加此變更到提交日誌(Commit log)以滿足事務性目的
使用此變更修改一個內存內的Memtable 結構

它的工作就此結束.這就是爲什麼Cassandra的寫操作如此快的原因:最慢的部分就是追加變更日誌到文件的操作.與關係型數據庫不同的是,Cassandra不會修改存儲在磁盤上的數據,也不會去更新索引,因此沒有密集的同步磁盤操作來阻塞這次寫操作.

還有多個定期發生的異步操作:
當Memtable結構數據滿的時候需要寫入到SSTable,一個基於磁盤的結構,因此我們不會有太多隻存在於內存的數據.
每個給定列族(ColumnFamily)的一組臨時的SSTable會被合併到一個大的SSTable.此時,臨時的SSTable就沒有用了,它們會在將來的某個時間點被當作垃圾回收掉.

讀操作與一致性

Brewer的CAP定理是分佈式系統中的一個基本定理:分佈式系統可以有一致性(Consistency)、可用性(Availability)以及分區容錯性(Partition-tolerance)這三種屬性,但是隻能同時確保其中的兩個屬性.Cassandra中,保證可用性A+容錯性P並弱化一致性. 考慮下面這種情況,讀操作與寫操作在時間上非常接近.假設你擁有一個Key “A”,它的值在你的集羣中爲“123”.現在,你將“A”更新爲“456”.寫操作被髮送到N個不同的節點,每個節點都耗費部分時間來寫這個值.現在,你發送一個讀取Key “A”的請求.這些節點中的某些節點中這個Key對應的值可能仍然爲“123”,而同時節點中的其他節點此Key的值爲“456”.他們最終都會返回“456”,但並不能保證什麼時候可以做到(在實踐中,通常是幾個毫秒的時間).接下來你就會發現爲什麼這一點很重要.

在你的客戶端,讀操作與寫操作類似,客戶端發送一個讀請求到Cassandra集羣中的任一隨機節點(也就是存儲代理,Storage Proxy).這個代理確定持有需要被讀取數據的N份拷貝所在的環上的節點(根據複製放置策略),並對每一個節點發出一個讀請求.由於最終一致性的限制,Cassadra允許客戶端選擇讀一致性的強度:

單一讀 – 代理返回它獲得的第一份響應. 這樣很可能返回過期的數據.
仲裁數讀取 – 代理等待一個簡單多數返回同樣的數據.這樣讀取過期數據(除非有節點宕機)會變得更加困難,但也更慢了.
在後臺,代理還會對任何不一致的響應執行讀修復(read repair).代理會往任何返回較早的值的節點發送一個寫請求,以確保這些節點在將來可以返回最新的值.

掃描範圍

作爲鍵值存儲(Key/value store),Cassandra運轉良好:給定一個鍵(Key),它就會爲你返回這個鍵(Key)對應的值(Value).但這通常還不足以回答關鍵的問題:如果我想要讀取所有姓(last name)從Z開始用戶?或者讀取所有在2010年2月1日到2010年3月1日之間下的訂單?要回答這些問題,Cassandra必須知道如何來確定持有這些相關值的節點.這個工作是由分割器(Partitioner)來完成的.默認情況下,Cassandra會使用RandomPartitioner(隨機分割器),它可以確保將負載均勻地分佈在集羣上,但是無法使用它來做範圍掃描.作爲替代,一個列族(Column Family)可以配置使用OrderPreservingPartitioner(保留順序的分割器),它知道如何將一個範圍的鍵(Key)映射(map)到一個或多個節點上.實際上,它知道哪個(些)節點持有你的按字母排序的用戶的數據以及哪個(些)節點持有二月份的訂單.

單一節點上的讀取操作

因此,將分佈式系統的所有胡說八道都放在一邊,當執行讀操作時每個節點在做什麼? 回想一下, Cassandra有兩個級別的存儲:Memtable與SSTable. 從Memtable中讀取相對無痛 – 我們是在內存中進行操作,數據量也相對較小,在這些內容中循環查找儘可能很快.掃描SSTable時,Cassandra使用一個更低級別的列索引與布隆過濾器(bloom filter)來查找磁盤上的必要的數據塊,對數據進行反序列化(deserialize),並確定需要返回的真實數據. 這裏會發生大量的磁盤IO,因此最終造成的讀延時會比類似的DBMS還要高. Cassandra提供了部分行緩存(row caching),它確實解決大部分的延時.

閒話協議(Gossip)
Cassandra是一個有單個節點組成的集羣 – 其中沒有“主”節點或單點故障-因此,每個節點都必須積極地確認集羣中其他節點的狀態。它們使用一個稱爲閒話(Gossip)的機制來做此事.每個節點每秒中都會將集羣中每個節點的狀態“以閒話的方式傳播”到1-3個其他節點.系統爲閒話數據添加了版本,因此一個節點的任何變更都會快速地傳播遍整個集羣.通過這種方式,每個節點都能知道任一其他節點的當前狀態:是在正在自舉呢, 還是正常運行呢,等等.

提示移交(Hinted Handoff)
Cassandra會存儲數據的拷貝到N個節點.客戶端可以根據數據的重要性選擇一個一致性級別(Consistency level),例如, ConsistencyLevel.QUORUM表示,只有這N個節點中的多數返回成功才表示這個寫操作成功.

如果這些節點中的一個宕機了,會發生什麼呢?寫操作稍後將如何傳遞到此節點呢?Cassandra使用了一種稱爲提示移交(Hinted Handoff)的技術解決此問題,其中數據會被寫入並保存到另一個隨機節點X,並提示這些數據需要被保存到節點Y,並在節點重新在線時進行重放(記住,當節點Y變成在線時,閒話機制會快速通知X節點).提示移交可以確保節點Y可以快速的匹配上集羣中的其他節點.注意,如果提示移交由於某種原因沒有起作用,讀修復最終仍然會“修復”這些過期數據,不過只有當客戶端訪問這些數據時纔會進行讀修復.

提示的寫是不可讀的(因爲節點X並不是這N份拷貝的其中一個正式節點),因此,它們並不會記入寫一致性.如果Cassandra的配置了3份拷貝,而其中的兩個節點不可用,就不可能實現一個ConsistencyLevel.QUORUM的寫操作.

逆熵(Anti-Entropy)
Cassandra的最後一個衆所周知的祕密武器是逆熵(Anti-entropy).逆熵明確保證集羣中的節點一致認可當前數據.如果由於默認情況,讀修復(read repair)與提示移交(hinted handoff)都沒有生效,逆熵會確保節點達到最終一致性.逆熵服務是在“主壓縮”(等價與關係數據庫中的重建表)時運行的,因此,它是一個相對重量級但運行不頻繁的進程.逆熵使用Merkle樹(也稱爲散列樹)來確定節點在列族(column family)數據樹內的什麼位置不能一致認可,接着修復該位置的每一個分支

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章