- 1. 背景介紹
Cassandra 使用分佈式哈希表(DHT)來確定存儲某一個數據對象的節點。在 DHT 裏面,負責存儲的節點以及數據對象都被分配一個 token。token 只能在一定的範圍內取值,比如說如果用 MD5 作爲 token 的話,那麼取值範圍就是 [0, 2^128-1]。存儲節點以及對象根據 token 的大小排列成一個環,即最大的 token 後面緊跟着最小的 token,比如對 MD5 而言,token 2^128-1 的下一個 token 就是 0。Cassandra 使用以下算法來分佈數據:
首先,每個存儲節點被分配一個隨機的 token(涉及數據分區策略),該 token 代表它在 DHT 環上的位置;
然後,用戶爲數據對象指定一個 key(即 row-key),Cassandra 根據這個 key 計算一個哈希值作爲 token,再根據 token 確定對象在 DHT 環上的位置;
最後,該數據對象由環上擁有比該對象的 token 大的最小的 token 的節點來負責存儲;
根據用戶在配置時指定的備份策略(涉及網絡拓撲策略),將該數據對象備份到另外的 N-1 個節點上。網絡中總共存在該對象的 N 個副本。
因此,每個存儲節點最起碼需要負責存儲在環上位於它與它的前一個存儲節點之間的那些數據對象,而且這些對象都會被備份到相同的節點上。我們把 DHT 環上任何兩點之間的區域稱爲一個 range,那麼每個存儲節點需要存儲它與前一個存儲節點之間的 range。
因爲 Cassandra 以 range 爲單位進行備份,所以每個節點需要定期檢查與它保存了相同的 range 的節點,看是否有不一致的情況,這涉及到數據一致性策略。
另外,Cassandra的一個特點是寫速度大於讀速度,這都歸功於它的存儲策略。
本文總結了Cassandra中使用的各種策略,包括數據分局策略,數據備份策略,網絡拓撲策略,數據一致性策略和存儲策略等。
2. Partitioner數據分區策略
將key/value按照key存放到不同的節點上。Partitioner會按照某種策略給每個cassandra節點分配一個token,每個key/value進行某種計算後,將被分配到器對應的節點上。
提供了以下幾個分佈策略:
org.apache.cassandra.dht.RandomPartitioner:
將key/value按照(key的)md5值均勻的存放到各個節點上。由於key是無序的,所有該策略無法支持針對Key的範圍查詢。
org.apache.cassandra.dht.ByteOrderedPartitioner(BOP):
將key/value按照key(byte)排序後存放到各個節點上。該Partitioner允許用戶按照key的順序掃描數據。該方法可能導致負載不均衡。
org.apache.cassandra.dht.OrderPreservingPartitioner:
該策略是一種過時的BOP,僅支持key爲UTF8編碼的字符串。
org.apache.cassandra.dht.CollatingOrderPreservingPartitioner:
該策略支持在EN或者US環境下的key排序方式。
3. 備份策略(副本放置策略)
爲了保證可靠性,一般要將數據寫N份,其中一份寫在其對應的節點上(由數據分片策略決定),另外N-1份如何存放,需要有相應的備份策略。
SimpleStrategy (以前稱爲RackUnawareStrategy,對應org.apache.cassandra.locator.RackUnawareStrategy):
不考慮數據中心,將Token按照從小到大的順序,從第一個Token位置處依次取N個節點作爲副本。
OldNetworkTopologyStrategy (以前稱爲RackAwareStrategy,對應org.apache.cassandra.locator.RackAwareStrategy):
考慮數據中心,首先,在primary Token所在的數據中心的不同rack上存儲N-1份數據,然後在另外一個不同數據中心的節點存儲一份數據。該策略特別適合多個數據中心的應用場景,這樣可以犧牲部分性能(數據延遲)的前提下,提高系統可靠性。
NetworkTopologyStrategy (以前稱爲DatacenterShardStrategy,對應org.apache.cassandra.locator.DataCenterShardStrategy):
這需要複製策略屬性文件,在該文件中定義在每個數據中心的副本數量。在各數據中心副本數量的總和應等於Keyspace的副本數量。
4. 網絡拓撲策略
該策略主要用於計算不同host的相對距離,進而告訴Cassandra你的網絡拓撲結構,以便更高效地對用戶請求進行路由。
org.apache.cassandra.locator.SimpleSnitch:
將不同host邏輯上的距離(cassandra ring)作爲他們之間的相對距離。
org.apache.cassandra.locator.RackInferringSnitch:
相對距離是由rack和data center決定的,分別對應ip的第3和第2個八位組。即,如果兩個節點的ip的前3個八位組相同,則認爲它們在同一個rack(同一個rack中不同節點,距離相同);如果兩個節點的ip的前兩個八位組相同,則認爲它們在同一個數據中心(同一個data center中不同節點,距離相同)。
org.apache.cassandra.locator.PropertyFileSnitch:
相對距離是由rack和data center決定的,且它們是在配置文件cassandra-topology.properties中設置的。
5. 調度策略
對用戶請求採用某個策略調度到不同節點上。
org.apache.cassandra.scheduler.NoScheduler:不用調度器
org.apache.cassandra.scheduler.RoundRobinScheduler:不同request_scheduler_id的用戶請求採用輪詢的方法放到節點的不同隊列中。
6. 一致性策略
6.1 一致性等級
Cassandra採用了最終一致性。最終一致性是指分佈式系統中的一個數據對象的多個副本儘管在短時間內可能出現不一致,但是經過一段時間之後,這些副本最終會達到一致。
Cassandra 的一個特性是可以讓用戶指定每個讀/插入/刪除操作的一致性級別(consistency level)。Casssandra API 目前支持以下幾種一致性級別:
ZERO:只對插入或者刪除操作有意義。負責執行操作的節點把該修改發送給所有的備份節點,但是不會等待任何一個節點回復確認,因此不能保證任何的一致性。
ONE:對於插入或者刪除操作,執行節點保證該修改已經寫到一個存儲節點的 commit log 和 Memtable 中;對於讀操作,執行節點在獲得一個存儲節點上的數據之後立即返回結果。
QUORUM:假設該數據對象的備份節點數目爲 n。對於插入或者刪除操作,保證至少寫到 n/2+1 個存儲節點上;對於讀操作,向 n/2+1 個存儲節點查詢,並返回時間戳最新的數據。
ALL:對於插入或者刪除操作,執行節點保證n(n爲replication factor)個節點插入或者刪除成功後才向client返回成功確認消息,任何一個節點沒有成功,該操作均失敗;對於讀操作,會向n個節點查詢,返回時間戳最新的數據,同樣,如果某個節點沒有返回數據,則認爲讀失敗。
注:Cassandra默認的讀寫模式W(QUORUM)/R(QUORUM),事實上,只要保證W+R>N(N爲副本數),即寫的節點和讀的節點重疊,則是強一致性. 如果W+R<=N ,則是弱一致性.(其中,W是寫節點數目,R是讀節點數目)。
如果用戶在讀和寫操作的時候都選擇 QUORUM 級別,那麼就能保證每次讀操作都能得到最新的更改。另外,Cassandra 0.6 以上的版本對插入和刪除操作支持 ANY 級別,表示保證數據寫到一個存儲節點上。與 ONE 級別不同的地方是,ANY 把寫到 hinted handoff 節點上也看作成功,而 ONE 要求必須寫到最終的目的節點上。
6.2 維護最終一致性
Cassandra 通過4個技術來維護數據的最終一致性,分別爲逆熵(Anti-Entropy),讀修復(Read Repair),提示移交(Hinted Handoff)和分佈式刪除。
(1) 逆熵
這是一種備份之間的同步機制。節點之間定期互相檢查數據對象的一致性,這裏採用的檢查不一致的方法是 Merkle Tree;
(2) 讀修復
客戶端讀取某個對象的時候,觸發對該對象的一致性檢查;
舉例:
讀取Key A的數據時,系統會讀取Key A的所有數據副本,如果發現有不一致,則進行一致性修復。
如果讀一致性要求爲ONE,會立即返回離客戶端最近的一份數據副本。然後會在後臺執行Read Repair。這意味着第一次讀取到的數據可能不是最新的數據;如果讀一致性要求爲QUORUM,則會在讀取超過半數的一致性的副本後返回一份副本給客戶端,剩餘節點的一致性檢查和修復則在後臺執行;如果讀一致性要求高(ALL),則只有Read Repair完成後才能返回一致性的一份數據副本給客戶端。可見,該機制有利於減少最終一致的時間窗口。
(3) 提示移交
對寫操作,如果其中一個目標節點不在線,先將該對象中繼到另一個節點上,中繼節點等目標節點上線再把對象給它;
舉例:
Key A按照規則首要寫入節點爲N1,然後複製到N2。假如N1宕機,如果寫入N2能滿足ConsistencyLevel要求,則Key A對應的RowMutation將封裝一個帶hint信息的頭部(包含了目標爲N1的信息),然後隨機寫入一個節點N3,此副本不可讀。同時正常複製一份數據到N2,此副本可以提供讀。如果寫N2不滿足寫一致性要求,則寫會失敗。 等到N1恢復後,原本應該寫入N1的帶hint頭的信息將重新寫回N1。
(4) 分佈式刪除
單機刪除非常簡單,只需要把數據直接從磁盤上去掉即可,而對於分佈式,則不同,分佈式刪除的難點在於:如果某對象的一個備份節點 A 當前不在線,而其他備份節點刪除了該對象,那麼等 A 再次上線時,它並不知道該數據已被刪除,所以會嘗試恢復其他備份節點上的這個對象,這使得刪除操作無效。Cassandra 的解決方案是:本地並不立即刪除一個數據對象,而是給該對象標記一個hint,定期對標記了hint的對象進行垃圾回收。在垃圾回收之前,hint一直存在,這使得其他節點可以有機會由其他幾個一致性保證機制得到這個hint。Cassandra 通過將刪除操作轉化爲一個插入操作,巧妙地解決了這個問題。
7. 存儲策略
Cassandra的存儲機制借鑑了Bigtable的設計,採用Memtable和SSTable的方式。和關係數據庫一樣,Cassandra在寫數據之前,也需要先記錄日誌,稱之爲commitlog(數據庫中的commit log 分爲 undo-log, redo-log 以及 undo-redo-log 三類,由於 cassandra採用時間戳識別新老數據而不會覆蓋已有的數據,所以無須使用 undo 操作,因此它的 commit log 使用的是 redo log),然後數據纔會寫入到Column Family對應的Memtable中,且Memtable中的數據是按照key排序好的。Memtable是一種內存結構,滿足一定條件後批量刷新(flush)到磁盤上,存儲爲SSTable。這種機制,相當於緩存寫回機制(Write-back Cache),優勢在於將隨機IO寫變成順序IO寫,降低大量的寫操作對於存儲系統的壓力。SSTable一旦完成寫入,就不可變更,只能讀取。下一次Memtable需要刷新到一個新的SSTable文件中。所以對於Cassandra來說,可以認爲只有順序寫,沒有隨機寫操作。
SSTable是不可修改的,且一般情況下,一個CF可能會對應多個SSTable,這樣,當用戶檢索數據時,如果每個SSTable均掃描一遍,將大大增加工作量。Cassandra爲了減少沒有必要的SSTable掃描,使用了BloomFilter,即通過多個hash函數將key映射到一個位圖中,來快速判斷這個key屬於哪個SSTable。
爲了減少大量SSTable帶來的開銷,Cassandra會定期進行compaction,簡單的說,compaction就是將同一個CF的多個SSTable合併成一個SSTable。在Cassandra中,compaction主要完成的任務是:
(1)垃圾回收: cassandra並不直接刪除數據,因此磁盤空間會消耗得越來越多,compaction 會把標記爲刪除的數據真正刪除;
(2)合併SSTable:compaction 將多個 SSTable 合併爲一個(合併的文件包括索引文件,數據文件,bloom filter文件),以提高讀操作的效率;
(3)生成 MerkleTree:在合併的過程中會生成關於這個 CF 中數據的 MerkleTree,用於與其他存儲節點對比以及修復數據。
cassandra中所使用的策略
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.