Cassandra內(nèi)部機制之讀操作
讀操作與一致性
Brewer的CAP定理是分布式系統(tǒng)中的一個基本定理:分布式系統(tǒng)可以有一致性(Consistency)、可用性(Availability)以及分區(qū)容錯性(Partition-tolerance)這三種屬性,但是只能同時確保其中的兩個屬性.在Cassandra中,他們保證AP并弱化一致性為眾所周知的最終一致性(Eventual Consistency). 考慮下面這種情況,讀操作與寫操作在時間上非常接近.假設你擁有一個Key “A”,它的值在你的集群中為“123”.現(xiàn)在,你將“A”更新為“456”.寫操作被發(fā)送到N個不同的節(jié)點,每個節(jié)點都耗費部分時間來寫這個值.現(xiàn)在,你發(fā)送一個讀取Key “A”的請求.這些節(jié)點中的某些節(jié)點中這個Key對應的值可能仍然為“123”,而同時節(jié)點中的其他節(jié)點此Key的值為“456”.他們最終都會返回“456”,但并不能保證什么時候可以做到(在實踐中,通常是幾個毫秒的時間).接下來你就會發(fā)現(xiàn)為什么這一點很重要.
在你的客戶端,讀操作與寫操作類似,客戶端發(fā)送一個讀請求到Cassandra集群中的任一隨機節(jié)點(也就是存儲代理,Storage Proxy).這個代理確定持有需要被讀取數(shù)據(jù)的N份拷貝所在的環(huán)上的節(jié)點(根據(jù)復制放置策略),并對每一個節(jié)點發(fā)出一個讀請求.由于最終一致性的限制,Cassadra允許客戶端選擇讀一致性的強度:
◆單一讀 – 代理返回它獲得的第一份響應. 這樣很可能返回過期的數(shù)據(jù).
◆仲裁數(shù)讀取 – 代理等待一個簡單多數(shù)返回同樣的數(shù)據(jù).這樣讀取過期數(shù)據(jù)(除非有節(jié)點宕機)會變得更加困難,但也更慢了.
在后臺,代理還會對任何不一致的響應執(zhí)行讀修復(read repair).代理會往任何返回較早的值的節(jié)點發(fā)送一個寫請求,以確保這些節(jié)點在將來可以返回最新的值.下面是部分邊緣狀況,我不清楚Cassandra是如何進行處理的:
◆如果有偶數(shù)個節(jié)點有回應,其中一半返回值“X”而另一半返回值“Y”?由于每個列的值都有時間戳,我推測它可能會使用時間戳來做最后的裁判.
◆如果有兩個包含舊時間戳的節(jié)點返回“X”而一個有新時間戳的節(jié)點返回“Y”,Cassandra該如何處理? 多數(shù)會覆蓋時鐘嗎?
◆如果集群的節(jié)點的始終不同步,Cassandra會如何操作?
掃描范圍
作為鍵值存儲(Key/value store),Cassandra運轉良好:給定一個鍵(Key),它就會為你返回這個鍵(Key)對應的值(Value).但這通常還不足以回答關鍵的問題:如果我想要讀取所有姓(last name)從Z開始用戶?或者讀取所有在2010年2月1日到2010年3月1日之間下的訂單?要回答這些問題,Cassandra必須知道如何來確定持有這些相關值的節(jié)點.這個工作是由分割器(Partitioner)來完成的.默認情況下,Cassandra會使用RandomPartitioner(隨機分割器),它可以確保將負載均勻地分布在集群上,但是無法使用它來做范圍掃描.作為替代,一個列族(Column Family)可以配置使用OrderPreservingPartitioner(保留順序的分割器),它知道如何將一個范圍的鍵(Key)映射(map)到一個或多個節(jié)點上.實際上,它知道哪個(些)節(jié)點持有你的按字母排序的用戶的數(shù)據(jù)以及哪個(些)節(jié)點持有二月份的訂單.
單一節(jié)點上的讀取操作
因此,將分布式系統(tǒng)的所有胡說八道都放在一邊,當執(zhí)行讀操作時每個節(jié)點在做什么? 回想一下, Cassandra有兩個級別的存儲:Memtable與SSTable. 從Memtable中讀取相對無痛 – 我們是在內(nèi)存中進行操作,數(shù)據(jù)量也相對較小,在這些內(nèi)容中循環(huán)查找盡可能很快.掃描SSTable時,Cassandra使用一個更低級別的列索引與布隆過濾器(bloom filter)來查找磁盤上的必要的數(shù)據(jù)塊,對數(shù)據(jù)進行反序列化(deserialize),并確定需要返回的真實數(shù)據(jù). 這里會發(fā)生大量的磁盤IO,因此最終造成的讀延時會比類似的DBMS還要高. Cassandra提供了部分行緩存(row caching),它確實解決大部分的延時.
這篇文章時一個Cassandra的讀取路徑的旋風之旅. 要想知道更多關于此主題的內(nèi)容,請參考存儲配置(StorageConfiguration)的維基文章. 我將在下篇文章中介紹Cassandra中的部分技巧(訣竅), Cassandra用它們來解決分布式系統(tǒng)內(nèi)置的無數(shù)邊緣狀況
原文鏈接:http://www.dbthink.com/?p=432