Cassandra是點對點分布式系統,叢集中各節點平等,資料分布于叢集中各節點,各節點間每秒交換一次資訊。每個節點的commit log捕獲寫操作來確定資料持久性。資料先被寫入memtable-記憶體中的資料結構,待該結構滿後資料被寫入SSTable-硬碟中的資料檔案。所有的寫内容被自動在叢集中分區并複制。
Cassandra資料庫面向行。授權使用者可連接配接至任意資料中心的任意節點,并通過類似SQL的CQL查詢資料。叢集中,一個應用一般包含一個keyspace,一個keyspace中包含多個表。
用戶端連接配接到某一節點發起讀或寫請求時,該節點充當用戶端應用與擁有相應資料的節點間的協調者(coordinator)以根據叢集配置确定環中的哪個節點當擷取這個請求。
關鍵詞
Ø Gossip:點對點通信協定,用以Cassandra叢集中節點間交換位置和狀态資訊。
Ø Partitioner:決定如何在叢集中的節點間分發資料,也即在哪個節點放置資料的第一個replica。
Ø Replica placement strategy:決定在哪些節點放置資料的其他replica。Cassandra在叢集中的多個節點存儲資料的多份拷貝-replicas來確定可靠和容錯。
Ø Snitch:定義了複制政策用來放置replicas和路由請求所使用的拓撲資訊
Ø cassandra.yaml檔案:Cassandra主配置檔案
Ø system: Cassandra的系統keyspace,存放table、keyspace的屬性資訊等。而屬性資訊可通過CQL或其他驅動設定。
1. 節點間通信
Cassandra使用點對點通訊協定gossip在叢集中的節點間交換位置和狀态資訊。gossip程序每秒運作一次,與至多3個其他節點交換資訊,這樣所有節點可很快了解叢集中的其他節點資訊。
配置gossip(在cassandra.ymal中設定)
Ø cluster_name:節點所屬叢集名,叢集中每個節點應相同。
Ø listen_address:供其他節點連接配接至該節點的IP位址或主機名,當由localhost設為公共位址。
Ø seed_provider:逗号分隔的IP位址(種子清單),gossip通過種子節點學習環的拓撲,叢集中各節點種子清單當相同。多資料中心叢集中每個資料中心的種子清單當至少包含一個該中心内的節點。
Ø storage_port:節點間通訊端口,叢集中各節點當一緻。
Ø initial_token:用于single-node-per-token結構,節點在環空間隻擁有一段連續的token範圍。
Ø num_tokens:用于virtual nodes,定義了節點在環空間所擁有的随機配置設定的token數目。
失敗檢測與恢複
Ø gossip可檢測其他節點是否正常以避免将請求路由至不可達或者性能差的節點(後者需配置為dynamic snitch方可)。
Ø 可通過配置phi_convict_threshold來調整失敗檢測的敏感度。
Ø 對于失敗的節點,其他節點會通過gossip定期與之聯系以檢視是否恢複而非簡單将之移除。若需強制添加或移除叢集中節點需使用nodetool工具。
Ø 一旦某節點被标記為失敗,其錯過的寫操作會有其他replicas存儲一段時間(需開啟hinted handoff,若節點失敗的時間超過了max_hint_window_in_ms,錯過的寫不再被存儲。)Down掉的節點經過一段時間恢複後需執行repair操作,一般在所有節點運作nodetool repair以確定資料一緻。
2. 資料複制和分發
Cassandra中分發、複制同時進行。Cassandra被設計為點對點系統,會建立資料的多個副本存儲在叢集中的一組節點中。Cassandra中資料被組織為表,由primary key辨別,primary key決定資料将被存儲在哪個節點。
需指定的内容
Virtual nodes:指定資料與實體節點的所屬關系
Partitioner:在叢集内劃分資料
Replicationstrategy:決定如何處理每行資料的replicas
Snitch:定義replicationstrategy放置資料的replicas時使用的拓撲資訊
一緻性哈希
表中每行資料由primary key辨別,Cassandra為每個primarykey配置設定一個hash值,叢集中每個節點擁有一個或多個hash值區間。這樣便可根據primary key對應的hash值将該條資料放在包含該hash值的hash值區間對應的節點中。
虛拟節點
使用虛拟節點控制資料在叢集中的分布

若不使用虛拟節點則需手工為叢集中每個節點計算和配置設定一個token。每個token決定了節點在環中的位置以及節點應當承擔的一段連續的資料hash值的範圍。如上圖上半部分,每個節點配置設定了一個單獨的token代表環中的一個位置,每個節點存儲将row key映射為hash值之後落在該節點應當承擔的唯一的一段連續的hash值範圍内的資料。每個節點也包含來自其他節點的row的副本。而是用虛拟節點允許每個節點擁有多個較小的不連續的hash值範圍。如上圖中下半部分,叢集中的節點是用了虛拟節點,虛拟節點随機選擇且不連續。資料的存放位置也由row key映射而得的hash值确定,但是是落在更小的分區範圍内。
使用虛拟節點的好處
Ø 無需為每個節點計算、配置設定token
Ø 添加移除節點後無需重新平衡叢集負載
Ø 重建死掉的節點更快
Ø 改善了在同一叢集使用異種機器
資料複制
Cassandra在多個節點中存放replicas以保證可靠性和容錯性。replicationstrategy決定放置replicas的節點。replicas的總數由複制因子- replication factor确定,比如因子為2代表每行有兩份拷貝,每份拷貝存儲在不同的節點中。所有的replicas無主從之分。replication factor通常不能超過叢集中節點總數。然而,可現增加replication facto之後在将節點增至期望的數量。當replication factor超過總結點數時,寫操作被拒絕,但讀操作可進行,隻要滿足期望的一緻性級别。
目前有兩種可用的複制政策:
Ø SimpleStrategy:僅用于單資料中心,将第一個replica放在由partitioner确定的節點中,其餘的replicas放在上述節點順時針方向的後續節點中。
Ø NetworkTopologyStrategy:可用于較複雜的多資料中心。可以指定在每個資料中心分别存儲多少份replicas。在每個資料中心放置replicas的方式類似于SimpleStrategy,但傾向于将replicas放在不同rack,因為同一rack的節點傾向于同時失敗。配置每個資料中心分别放置多少replicas時要考慮兩個主要方面:(1)可滿足本地讀而非跨資料中心讀;(2)失敗場景。兩種常用的配置方式為(1)每個資料中心兩份replicas,(2)每個資料中心3份replicas。當然,用于特殊目的的非對稱配置也是可以的,比如在讀操作較頻繁的資料中心配置3份replicas而在用于分析的資料中心配置一份replicas。
複制政策在建立keyspace時指定,如
CREATEKEYSPACE Excelsior WITH REPLICATION = { 'class' : 'SimpleStrategy','replication_factor' : 3 };
CREATEKEYSPACE "Excalibur" WITH REPLICATION = {'class' :'NetworkTopologyStrategy', 'dc1' : 3, 'dc2' : 2};
其中dc1、dc2這些資料中心名稱要與snitch中配置的名稱一緻。
3. Partitioners
在Cassandra中,table的每行由唯一的primarykey辨別,partitioner實際上為一hash函數用以計算primary key的token。Cassandra依據這個token值在叢集中放置對應的行。
三種partitioner(在cassandra.yaml中設定)
Ø Murmur3Partitioner:目前的預設值,依據MurmurHash哈希值在叢集中均勻分布資料。
Ø RandomPartitioner:依據MD5哈希值在叢集中均勻分布資料。
Ø ByteOrderedPartitioner:依據行key的位元組從字面上在叢集中順序分布資料。(不推薦使用)
Murmur3Partitioner和RandomPartitioner使用token向每個節點指派等量的資料進而将keyspace中的表均勻分布在環中,即使不同的表使用不同的primary key。讀寫請求均被均勻的分布。ByteOrderedPartitioner允許通過primary key順序掃描(可通過index達到同樣目的),但已引起如下問題(1)較複雜的負載均衡,(2)順序的寫易導緻熱點,(3)多表不均勻的負載均衡。
注意:若使用虛拟節點(vnodes)則無需手工計算tokens。若不使用虛拟節點則必須手工計算tokens将所得的值指派給cassandra.ymal主配置檔案中的initial_token參數。具體可參考:http://www.datastax.com/documentation/cassandra/2.0/webhelp/index.html#cassandra/architecture/../configuration/configGenTokens_c.html
4. Snitches
提供網絡拓撲資訊,用以确定向/從哪個資料中心或者網架寫入/讀取資料。
注意:(1)所有節點需用相同的snitch;(2)叢集中已插入資料後由更改了snitch則需運作一次fullrepair。
Ø Dynamic snitching
監控從不同replica讀操作的性能,選擇性能最好的replica。dynamic snitch預設開啟,所有其他snitch會預設使用dynamic snitch 層。
Ø SimpleSnitch
預設值,用于單資料中心部署,不使用資料中心和網架資訊。使用該值時keyspace複制政策中唯一需指定的是replication factor
Ø RackInferringSnitch
根據資料中心和網架确定節點位置,而資料中心及網架資訊又有節點的IP位址隐含訓示。
Ø PropertyFileSnitch
根據資料中心和網架确定節點位置,而網絡拓撲資訊又由使用者定義的配置檔案cassandra-topology.properties 擷取。在節點IP位址格式不統一無法隐含訓示資料中心及網架資訊或者複雜的複制組中使用該值。需注意的是:(1)配置檔案中資料中心名需與keyspace中複制政策中指定的資料中心名稱一緻;(2)配置檔案中需包含叢集中任一節點;(3)叢集中各節點内cassandra-topology.properties配置檔案需相同。
Ø GossipingPropertyFileSnitch
Ø 在cassandra-rackdc.properties配置檔案中定義本節點所屬的資料中心和網架,利用gossip協定與其他節點交換該資訊。若從PropertyFileSnitch切至該值,則需逐節點逐次更新值為GossipingPropertyFileSnitch以確定gossip有時間傳播資訊。
Ø EC2Snitch
用于部署在Amazon EC2中且所有節點在單個區域中的叢集。
Ø EC2MultiRegionSnitch
Ø 用于部署在AmazonEC2中,且節點跨多個區域的叢集。
5. 用戶端請求
client連接配接至節點并發出read/write請求時,該node充當client端應用與包含請求資料的節點(或replica)之間的協調者,它利用配置的partitioner和replicaplacement政策确定那個節點當擷取請求。
5.1寫請求
協調者(coordinator)将write請求發送到擁有對應row的所有replica節點,隻要節點可用便擷取并執行寫請求。寫一緻性級别(write consistency level)确定要有多少個replica節點必須傳回成功的确認資訊。成功意味着資料被正确寫入了commit log個memtable。
上例為單資料中心,11個節點,複制因子為3,寫一緻性等級為ONE的寫情況。
5.2多資料中心的寫請求
基本同上,但會在各資料中心分别選擇一個協調者以處理該資料中心内的寫請求。與client直接連接配接的coordinator節點隻需将寫請求發送到遠端資料中心的coordinator一個節點即可,剩餘的由該coordinator完成。若一緻性級别設定為ONE或者LOCAL_QUORUM則僅與直接協調者位于同一資料中心的節點需傳回成功确認。
上例為雙單資料中心,各11個節點,複制因子為6,寫一緻性等級為ONE的寫情況。
5.3讀請求
Ø 直接讀請求
Ø 背景讀修複請求
與直接讀請求聯系的replica數目由一緻性級别确定。背景讀修複請求被發送到沒有收到直接讀請求的額外的replica,以確定請求的row在所有replica上一緻。
協調者首先與一緻性級别确定的所有replica聯系,被聯系的節點傳回請求的資料,若多個節點被聯系,則來自各replica的row會在記憶體中作比較,若不一緻,則協調者使用含最新資料的replica向client傳回結果。
同時,協調者在背景聯系和比較來自其餘擁有對應row的replica的資料,若不一緻,會向過時的replica發寫請求用最新的資料進行更新。這一過程叫read repair。
上例為單資料中心,11個節點,複制因子為3,一緻性級别為QUORUM的讀情況。
6. 資料庫内部
6.1資料管理
使用類似Log-StructuredMerge Tree的存儲結構,而非典型的關系型資料庫使用的B-Tree結構。存儲引擎連續的将資料以追加的模式寫物磁盤并持續存儲資料。節點間/内的操作并行運作。因不使用B-Tree故無需協同控制,在寫時不必執行更新。Cassandra在SSD中性能表現極佳。
高吞吐量和低延遲
操作并行運作,吞吐量和延遲互相獨立。log-structured設計避免詢盤開銷。去除on-disk資料修改,省時且延長SSD壽命。無on-disk型的資料修改故無需鎖定寫請求這樣的協同控制。無主、從,在所有節點運作同樣的代碼。
單獨的表目錄
/var/lib/cassandra/data/ks1/cf1/ks1-cf1-ja-1-Data.db
其中/var/lib/cassandra/data/為cassandra.yaml中指定的資料檔案目錄。ks1為keyspace名cf1/為columnfamilies名。這樣可将表連接配接至標明的目标位置以便于将活躍的表移到更快的存儲媒體,或者将表分不到多個可用的儲存設備以均衡負載
6.2關于寫
複制的角色
通過在多個同級節點建立資料的多個副本保證可靠性和容錯。表是非關系型的,無需過多額外工作來維護關聯的表的完整性,是以寫操作較關系型資料庫快很多。
寫過程
先将資料寫進記憶體中的資料結構memtable,同時追加到磁盤中的commitlog中。表使用的越多,對應的memtable應越大,cassandra動态的為memtable配置設定記憶體,也可自己手工指定。memtable内容超出指定容量後memtable資料(包括索引)被放進将被刷入磁盤的隊列,可通過memtable_flush_queue_size配置隊列長度。若将被刷入磁盤的資料超出了隊列長度,cassandra會鎖定寫。memtable表中的資料由連續的I/O刷進磁盤中的SSTable,之後commit log被清空。每個表有獨立的memtable和SSTable。
6.3關于更新、删除和hinted handoff writes
更新(cassandra中插入重複的primarykey也被看做是更新操作)
不直接在磁盤中原地更新而是先在memtable進行所有的更新。最後更新内容被刷入磁盤存儲在新的SSTable中,僅當column的時間戳比既存的column更新時才覆寫原來的資料。
删除
Ø 不會立即從磁盤移除删除的資料
被删除的資料會被tombstone标記以指定其狀态,它會存在一定的時間(由gc_grace_seconds指定),超出該時間後compaction程序永久删除該column。
Ø 若不例行性的執行節點repair操作,被删除的column可能重新出現
若删除期間節點down掉,被标記為tombstone的column會發送信号給Cassandra使其重發删除請求給該replica節點。若replica在gc_grace_seconds期間複活,會最終受到删除請求,若replica在gc_grace_seconds之後複活,節點可能錯過删除請求,而在節點恢複後立即删除資料。需定期執行節點修複操作來避免删除資料重制。
hinted handoff writes
在不要求一緻性時確定寫的高可用,在cassandra.yaml中開啟該功能。執行write操作時若擁有對應row的replica down掉了或者無回應,則協調者會在本地的system.hints表中存儲一個hint,訓示該寫操作需在不可用的replica恢複後重新執行。預設hints儲存3小時,可通過max_hint_window_in_ms改變該值。
提示的write不計入consistencylevel中的ONE,QUORUM或ALL,但計入ANY。ANY一緻性級别可確定cassandra在所有replica不可用時仍可接受write,并且在适當的replica可用且收到hint重放後該write操作可讀。
移除節點後節點對應的hints自動移除,删除表後對應的hints也會被移除。
仍需定期執行repair(避免硬體故障造成的資料丢失)
6.4關于讀
從SSD并行随機讀取,延時極低(不推薦cassandra使用轉盤式硬碟)。以partition key讀/寫,消除了關系型資料庫中複雜的查詢。
讀SSTable
首先檢查Bloom filter,每個SSTable都有一個Bloomfilter,用以在進行任何磁盤I/O前檢查請求的partition key對應的資料在SSTable中存在的可能性。若資料很可能存在,則檢查Partition key cache(Cassandra表partition index的緩存),之後根據index條目是否在cache中找到而執行不同步驟:
Ø 找到
從compression offset map中查找擁有對應資料的壓縮快。
從磁盤取出壓縮的資料,傳回結果集。
Ø 未找到
搜尋Partition summary(partition index的樣本集)确定index條目在磁盤中的近似位置。
從磁盤中SSTable内取出index條目。
從compression offset map中查找擁有對應資料的壓縮快。
從磁盤取出壓縮的資料,傳回結果集。
回顧插入/更新資料
讀的過程
由insert/update過程可知,read請求到達某一節點後,必須結合所有包含請求的row中的column的SSTable以及memtable來産生請求的資料。
例如,要更新包含使用者資料的某個row中的email 列,cassandra并不重寫整個row到新的資料檔案,而僅僅将新的email寫進新的資料檔案,username等仍處于舊的資料檔案中。上圖中紅線表示Cassandra需要整合的row的片段用以産生使用者請求的結果。為節省CPU和磁盤I/O,Cassandra會緩存合并後的結果,且可直接在該cache中更新row而不用重新合并。
6.5關于事務和協同控制
不支援RDBMS中具有復原和鎖定機制的ACID事務,但提供了一定程度的原子性(行級)、隔離性(行級)、持久性和eventual/tunable 類型的一緻性(因不支援連接配接和外鍵,故不提供ACID場景下的一緻性)。
Ø 原子性
row-level,對一個row的插入/更新被當做一個原子操作。不支援要麼都做要麼都不做的多行插入/更新。不支援在一個replica上write成功而在其他replica上write失敗的復原。用時間戳确定column的最新更新。若多個session同時更新同樣的column則使用最近的更新。
Ø 一緻性
l Tuneable一緻性
提供partition容錯。使用者可以以單個操作為基礎決定需多少個節點接收DML操作或響應SELECT操作。
l Linearizable一緻性
l 輕量事務(compare-and-set)的一系列隔離級别。在tuneable一緻性不足以滿足要求時使用,如執行無間斷的相繼操作或同時/不同時運作一個操作産生同樣的結果。Cassandra2.0使用類似2-phase commit的Paxos consensus協定實作Linearizable一緻性。(為支援該一緻性引入了SERIAL類型的consistency level及在CQL中使用了帶IF從句的輕量事務)
Ø 隔離性
Cassandra2.0開始支援row-level的隔離性。對行的寫操作在完成之前對其他使用者不可見。
Ø 持久性
同時将資料寫入記憶體中的memtable及磁盤中的commit log。伺服器故障時若memtable尚未刷入磁盤,在故障恢複後可重放commit log恢複丢失資料。這提供了本地持久性。資料在其他節點的副本加強了持久性。
輕量事務
Cassandra2.0中引入,彌補Tuneable一緻性。
n INSERT INTO emp(empid,deptid,address,first_name,last_name) VALUES(102,14,'luoyang','Jane Doe','li') IF NOT EXISTS;
n UPDATE emp SET address = 'luoyang' WHERE empid = 103 and deptid = 16IF last_name='zhang';
6.6配置資料一緻性
Cassandra中,一緻性級别可配置,以确定請求的資料如何在不同的replica保持一緻性,進而平衡響應時間和資料精确性。
Ø 寫一緻性
指明在傳回确認至用戶端前,write操作必須成功的replica數。
l ANY:write至少在一個replica成功。即使所有replica 都down掉,在寫hinted handoff後write仍成功。在replica恢複後該write可讀。
l ONE:write必須成功寫入至少一個replica的commit log和memtable。
l TWO:至少兩個
l THREE:至少三個
l QUORUM:至少(replication_factor/ 2) + 1個
l LOCAL_QUORUM:至少(replication_factor/ 2) + 1個,且與協調者處于同一資料中心
l EACH_QUORUM:所有資料中心,至少(replication_factor/ 2) + 1個
l ALL:全部
l SERIAL:至少(replication_factor/ 2) + 1個,用于達成輕量事務的linearizable consistency
需注意的是:實際上write還是會被發到所有相關的replica中,一緻性級别隻是确定必需要回報的replica數。
Ø 讀一緻性
指明在傳回資料值用戶端前,需要相應read請求的相關replica數。Cassandra從這些數量的replica中根據時間戳檢查最新的資料。級别同寫一緻性。
可通過cqlsh指令CONSISTENCY設定keyspace的一緻性,也可程式設計設定一緻性。