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