Cassandra原理介紹

Cassandra最初源自Facebook,結合了Google BigTable面向列的特性和[Amazon Dynamo](http://en.wikipedia.org/wiki/Dynamo(storagesystem) ) 分佈式哈希(DHT)的P2P特性於一身,具有很高的性能、可擴展性、容錯、部署簡單等特點。

它雖然有多的優點,但國內使用的公司貌似不多,遠沒有Hbase和MongoDB火,從百度指數上可以明顯看到這三個系統在國內的熱度對比。相對國內冷靜的市場來說,Cassandra在國外發展的倒是如火如荼,國外這個專門對數據庫進行評分的網站DB-Engines顯示Cassandra排進了前十名,比Hbase的名次要高好幾位,從2013年開始有了突飛猛進的增長,目前已有超過1500多家公司在使用Cassandra,可惜的是沒有多少國內公司,只有一家做雲存儲的創業公司雲諾名列榜單。這也證明了網上的中文資源爲何相對匱乏,我不得不找英文資料來看,倒是順便加強了我的英文閱讀能力,也算是失之東隅得之桑榆。

吸引我的特性

吸引我選擇Cassandra作爲NoSQL的原因主要有如下三點:

極高的讀寫性能

Cassandra寫數據時,首先會將請求寫入Commit Log以確保數據不會丟失,然後再寫入內存中的Memtable,超過內存容量後再將內存中的數據刷到磁盤的SSTable,並定期異步對SSTable做數據合併(Compaction)以減少數據讀取時的查詢時間。因爲寫入操作只涉及到順序寫入和內存操作,因此有非常高的寫入性能。而進行讀操作時,Cassandra支持像LevelDB一樣的實現機制,數據分層存儲,將熱點數據放在Memtable和相對小的SSTable中,所以能實現非常高的讀性能。

簡單的部署結構

相對Hbase等的主從結構,Cassandra是去中心化的P2P結構,所有節點完全一樣沒有單點,對小公司來說,我完全可以選擇數據複製份數爲2,先用兩三臺機器把Cassandra搭起來,既能保證數據的可靠性也方便今後機器的擴展,而Hbase起碼得四五臺機器吧。以後爲了更好地支持客戶可能需要在多個地方建立數據中心,而Cassandra對多數據中心的支持也很好,可以方便地部署多個數據中心,今早還看到一個俄羅斯最大電信公司的案例。另外我們的機器現在託管在一個小機房裏,萬一到時機器滿了無法增加要考慮搬遷機房時,使用多數據中心的方式也能做到無縫遷移。

和Spark的結合

Cassandra作爲一個底層的存儲系統,能夠方便地和Spark集成進行實時計算,這一點對我們的業務場景有致命的吸引力,我看到國外有很多使用案例就是用Spark+Cassandra來實現Velocity計算,比如Ooyala(需自備梯子)。

基本架構

Cassandra沒有像BigTable或Hbase那樣選擇中心控制節點,而選擇了無中心的P2P架構,網絡中的所有節點都是對等的,它們構成了一個環,節點之間通過P2P協議每秒鐘交換一次數據,這樣每個節點都擁有其它所有節點的信息,包括位置、狀態等,如下圖所示。

Cassandra Ring

客戶端可以連接集羣中的任一個節點,和客戶端建立連接的節點叫協作者(coordinator),它相當於一個代理,負責定位該次請求要發到哪些實際擁有本次請求所需數據的節點上去獲取,但如何獲取並返回,主要根據客戶端要求的一致性級別(Consistency Level)來定,比如:ONE指只要有一個節點返回數據就可以對客戶端做出響應,QUONUM指需要返回幾個根據用戶的配置數目,ALL指等於數據複製份數的所有節點都返回結果才能向客戶端做出響應,對於數據一致性要求不是特別高的可以選擇ONE,它是最快的一種方式。

Cassandra的核心組件包括:

Gossip:點對點的通訊協議,用來相互交換節點的位置和狀態信息。當一個節點啓動時就立即本地存儲Gossip信息,但當節點信息發生變化時需要清洗歷史信息,比如IP改變了。通過Gossip協議,每個節點定期每秒交換它自己和它已經交換過信息的節點的數據,每個被交換的信息都有一個版本號,這樣當有新數據時可以覆蓋老數據,爲了保證數據交換的準確性,所有的節點必須使用同一份集羣列表,這樣的節點又被稱作seed。

Partitioner:負責在集羣中分配數據,由它來決定由哪些節點放置第一份的copy,一般情況會使用Hash來做主鍵,將每行數據分佈到不同的節點上,以確保集羣的可擴展性。

Replica placement strategy:複製策略,確定哪個節點放置複製數據,以及複製的份數。

Snitch:定義一個網絡拓撲圖,用來確定如何放置複製數據,高效地路由請求。

cassandra.yaml:主配置文件,設置集羣的初始化配置、表的緩存參數、調優參數和資源使用、超時設定、客戶端連接、備份和安全

寫請求

當寫事件發生時,首先由Commit Log捕獲寫事件並持久化,保證數據的可靠性。之後數據也會被寫入到內存中,叫Memtable,當內存滿了之後寫入數據文件,叫SSTable,它是Log-Structured Storage Table的簡稱。 如果客戶端配置了Consistency Level是ONE,意味着只要有一個節點寫入成功,就由代理節點(Coordinator)返回給客戶端寫入完成。當然這中間有可能會出現其它節點寫入失敗的情況,Cassandra自己會通過Hinted Handoff或Read Repair 或者Anti-entropy Node Repair方式保證數據最終一致性。

對於多數據中心的寫入請求,Cassandra做了優化,每個數據中心選取一個Coordinator來完成它所在數據中心的數據複製,這樣客戶端連接的節點只需要向數據中心的一個節點轉發複製請求即可,由這個數據中心的Coordinator來完成該數據中心內的數據複製。

Cassandra的存儲結構類似LSM樹(Log-Structured Merge Tree)這種結構,不像傳統數據一般都使用B+樹,存儲引擎以追加的方式順序寫入磁盤連續存儲數據,寫入是可以併發寫入,不像B+樹一樣需要加鎖,寫入速度非常高,LevelDB、Hbase都是使用類似的存儲結構。

Commit Log記錄每次寫請求的完整信息,此時並不會根據主鍵進行排序,而是順序寫入,這樣進行磁盤操作時沒有隨機寫導致的磁盤大量尋道操作,對於提升速度有極大的幫助,號稱最快的本地數據庫LevelDB也是採用這樣的策略。Commit Log會在Memtable中的數據刷入SSTable後被清除掉,因此它不會佔用太多磁盤空間,Cassandra的配置時也可以單獨設置存儲區,這爲使用高性能但容量小价格昂貴的SSD硬盤存儲Commit Log,使用速度度但容量大價格非常便宜的傳統機械硬盤存儲數據的混合佈局提供了便利。

寫入到Memtable時,Cassandra能夠動態地爲它分配內存空間,你也可以使用工具自己調整。當達到閥值後,Memtable中的數據和索引會被放到一個隊列中,然後flush到磁盤,可以使用memtableflushqueue_size參數來指定隊列的長度。當進行flush時,會停止寫請求。也可以使用nodetool flush工具手動刷新 數據到磁盤,重啓節點之前最好進行此操作,以減少Commit Log回放的時間。爲了刷新數據,會根據partition key對Memtables進行重排序,然後順序寫入磁盤。這個過程是非常快的,因爲只包含Commit Log的追加和順序的磁盤寫入。

當memtable中的數據刷到SSTable後,Commit Log中的數據將被清理掉。每個表會包含多個Memtable和SSTable,一般刷新完畢,SSTable是不再允許寫操作的。因此,一個partition一般會跨多個SSTable文件,後續通過Compaction對多個文件進行合併,以提高讀寫性能。

這裏所述的寫請求不單指Insert操作,Update操作也是如此,Cassandra對Update操作的處理和傳統關係數據庫完全不一樣,並不立即對原有數據進行更新,而是會增加一條新的記錄,後續在進行Compaction時將數據再進行合併。Delete操作也同樣如此,要刪除的數據會先標記爲Tombstone,後續進行Compaction時再真正永久刪除。

讀請求

讀取數據時,首先檢查Bloom filter,每一個SSTable都有一個Bloom filter用來檢查partition key是否在這個SSTable,這一步是在訪問任何磁盤IO的前面就會做掉。如果存在,再檢查partition key cache,然後再做如下操作:

如果在cache中能找到索引,到compression offset map中找擁有這個數據的數據塊,從磁盤上取得壓縮數據並返回結果集。如果在cache中找不到索引,搜索partition summary確定索引在磁盤上的大概位置,然後獲取索引入口,在SSTable上執行一次單獨的尋道和一個順序的列讀取操作,下面也是到compression offset map中找擁有這個數據的數據塊,從磁盤上取得壓縮數據並返回結果集。讀取數據時會合並Memtable中緩存的數據、多個SSTable中的數據,才返回最終的結果。比如更新用戶Email後,用戶名、密碼等還在老的SSTable中,新的EMail記錄到新的SSTable中,返回結果時需要讀取新老數據並進行合併。

2.0之後的Bloom filter,compression offset map,partition summary都不放在Heap中了,只有partition key cache還放在Heap中。Bloom filter增長大約1~2G每billion partitions。partition summary是partition index的樣本,你可以通過index_interval來配置樣本頻率。compression offset map每TB增長1~3G。對數據壓縮越多,就會有越多個數的壓縮塊,和越大compression offset table。

讀請求(Read Request)分兩種,一種是Rirect Read Request,根據客戶端配置的Consistency Level讀取到數據即可返回客戶端結果。一種是Background Read Repair Request,除了直接請求到達的節點外,會被髮送到其它複製節點,用於修復之前寫入有問題的節點,保證數據最終一致性。客戶端讀取時,Coordinator首先聯繫Consistency Level定義的節點,發送請求到最快響應的複製節點上,返回請求的數據。如果有多個節點被聯繫,會在內存比較每個複製節點傳過的數據行,如果不一致選取最近的數據(根據時間戳)返回給客戶端,並在後臺更新過期的複製節點,這個過程被稱作Read Repair 。

下面是Consistency Level 爲ONE的讀取過程,Client連接到任意一個節點上,該節點向實際擁有該數據的節點發出請求,響應最快的節點數據回到Coordinator後,就將數據返回給Client。如果其它節點數據有問題,Coordinator會將最新的數據發送有問題的節點上,進行數據的修復。

Read One

數據整理(Compaction)

更新操作不會立即更新,這樣會導致隨機讀寫磁盤,效率不高,Cassandra會把數據順序寫入到一個新的SSTable,並打上一個時間戳以標明數據的新舊。它也不會立馬做刪除操作,而是用Tombstone來標記要刪除的數據。Compaction時,將多個SSTable文件中的數據整合到新的SSTable文件中,當舊SSTable上的讀請求一完成,會被立即刪除,空餘出來的空間可以重新利用。雖然Compcation沒有隨機的IO訪問,但還是一個重量級的操作,一般在後臺運行,並通過限制它的吞吐量來控制,`compactionthroughputmbpersec參數可以設置,默認是16M/s。另外,如果key cache顯示整理後的數據是熱點數據,操作系統會把它放入到page cache裏,以提升性能。它的合併的策略有以下兩種:

  • SizeTieredCompactionStrategy:每次更新不會直接更新原來的數據,這樣會造成隨機訪問磁盤,性能不高,而是在插入或更新直接寫入下一個sstable,這樣是順序寫入速度非常快,適合寫敏感的操作。但是,因爲數據分佈在多個sstable,讀取時需要多次磁盤尋道,讀取的性能不高。爲了避免這樣情況,會定期在後臺將相似大小的sstable進行合併,這個合併速度也會很快,默認情況是4個sstable會合並一次,合併時如果沒有過期的數據要清理掉,會需要一倍的空間,因此最壞情況需要50%的空閒磁盤。
  • LeveledCompactionStrategy:創建固定大小默認是5M的sstable,最上面一級爲L0下面爲L1,下面一層是上面一層的10倍大小。這種整理策略讀取非常快,適合讀敏感的情況,最壞只需要10%的空閒磁盤空間,它參考了LevelDB的實現,詳見LevelDB的具體實現原理

    這裏也有關於這兩種方式的詳細描述

數據複製和分發

數據分發和複製通常是一起的,數據用表的形式來組織,用主鍵來識別應該存儲到哪些節點上,行的copy稱作replica。當一個集羣被創建時,至少要指定如下幾個配置:Virtual Nodes,Partitioner,Replication Strategy,Snitch。

數據複製策略有兩種,一種是SimpleStrategy,適合一個數據中心的情況,第一份數據放在Partitioner確定的節點,後面的放在順時針找到的節點上,它不考慮跨數據中心和機架的複製。另外一種是NetworkTopologyStargegy,第一份數據和前一種一樣,第二份複製的數據放在不同的機架上,每個數據中心可以有不同數據的replicas。

Partitioner策略有三種,默認是Murmur3Partitioner,使用MurmurHash。RandomPartitioner,使用Md5 Hash。ByteOrderedPartitioner使用數據的字節進行有順分區。Cassandra默認使用MurmurHash,這種有更高的性能。

Snitch用來確定從哪個數據中心和哪個機架上寫入或讀取數據,有如下幾種策略:

  • DynamicSnitch:監控各節點的執行情況,根據節點執行性能自動調節,大部分情況推薦使用這種配置
  • SimpleSnitch:不會考慮數據庫和機架的情況,當使用SimpleStategy策略時考慮使用這種情況
  • RackInterringSnitch:考慮數據庫中和機架
  • PropertyFileSnitch:用cassandra-topology.properties文件來自定義
  • GossipPropertyFileSnitch:定義一個本地的數據中心和機架,然後使用Gossip協議將這個信息傳播到其它節點,對應的配置文件是cassandra-rockdc.properties

失敗檢測和修復(Failure detection and recovery)

Cassandra從Gossip信息中確認某個節點是否可用,避免客戶端請求路由到一個不可用的節點,或者執行比較慢的節點,這個通過dynamic snitch可以判斷出來。Cassandra不是設定一個固定值來標記失敗的節點,而是通過連續的計算單個節點的網絡性能、工作量、以及其它條件來確定一個節點是否失敗。節點失敗的原因可能是硬件故障或者網絡中斷等,節點的中斷通常是短暫的但有時也會持續比較久的時間。節點中斷並不意味着這個節點永久不可用了,因此不會永久地從網絡環中去除,其它節點會定期通過Gossip協議探測該節點是否恢復正常。如果想永久的去除,可以使用nodetool手工刪除。

當節點從中斷中恢復過來後,它會缺少最近寫入的數據,這部分數據由其它複製節點暫爲保存,叫做Hinted Handoff,可以從這裏進行自動恢復。但如果節點中斷時間超過maxhintwindowinms(默認3小時)設定的值,這部分數據將會被丟棄,此時需要用nodetool repair在所有節點上手工執行數據修復,以保證數據的一致性。

動態擴展

Cassandra最初版本是通過一致性Hash來實現節點的動態擴展的,這樣的好處是每次增加或減少節點只會影響相鄰的節點,但這個會帶來一個問題就是造成數據不均勻,比如新增時數據都會遷移到這臺機的機器上,減少時這臺機器上的數據又都會遷移到相鄰的機器上,而其它機器都不能分擔工作,勢必會造成性能問題。從1.2版本開始,Cassandra引入了虛擬節點(Virtual Nodes)的概念,爲每個真實節點分配多個虛擬節點(默認是256),這些節點並不是按Hash值順序排列,而是隨機的,這樣在新增或減少一個節點時,會有很多真實的節點參與數據的遷移,從而實現了負載勻衡。

推薦配置

  • 內存:16G~64G,越大越好,減少讀取和寫入磁盤的次數
  • CPU:8Core+ ,高併發請求
  • Disk:寫commit log和flush memtable 到 sstable時需要訪問磁盤,用SSD放commit log並不能提升寫性能,放數據會有顯著的提升但成本太高,理想的方式是使用SSD做Row Cache的二級索引,參見這個這篇文章:Cassandra:An SSD Boosted Key-Value Store (需自備梯子)
  • RAID:數據盤不需要做RAID,Cassandra本身就要複製多份數據到不同的機器上,並且有JBOD機制可以自動檢測損壞的磁盤
  • 文件系統:XFS,64位上接近無限大,Ext4 64位上最大支持16T
  • 網卡:千兆網卡

更多請參考:Apache Cassandra 2.0 Document



原文鏈接:http://yikebocai.com/2014/06/cassandra-principle/




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