介紹Cassandra基本觀念, schema設計與一些常見的名詞解釋 |
RMDB與Cassandra設計理念的差異
- 沒有join操作- 一種是在client執行merge一種是另外建立一張去正規化的table
- 去正規化 - C*在去正規化的狀況下效能最好
- 先定義好的欄位順序 - 查詢修改的參數必須按照key定義的順序
- 查詢優先 - RMDBS只要恰當的建立模型, 即便使用複雜的query都能查出資料, Cassandra會重探討查詢開始, 先思考系統查詢路徑與功能, 與RMDBS先建立table相反, 舉例來說訂房網站, 先定義出查詢功能:
- 搜尋景點附近的飯店
- 搜尋飯店資訊
- 搜尋飯店附近的景點
- 搜尋否段時間的訂房
- 搜尋否房間的評價
- 透過確認碼搜尋訂房資訊
- 透過飯店名稱.日期.客戶搜尋訂房資訊
- 透過客戶名稱搜尋所有訂房資訊
- 檢視客戶資訊
- 當設計完畢後可使用Chebotko圖畫出所有流程
- CQL範例請點此
CREATE TABLE hotel.hotels_by_poi (
poi_name text
hotel_id text,
name text,
phone text,
address frozen<address>,
PRIMARY KEY ((poi_name), hotel_id)) WITH comment = 'Q1. Find hotels near given poi'
AND CLUSTERING ORDER BY (hotel_id ASC, name DESC) ;
- comment為每張表註解
- address為自訂Type, 若要使用集合包起來, 必須與frozen搭配使用
- order by 要使用非partition key
- CLUSTERING ORDER BY (hotel_id ASC) : 預設的排序, query時可以調整遞增或遞減
- WITH CLUSTERING ORDER BY 插入時即會排序, 使得查詢變快速, 不設也可以排序
select * from hotel.hotels_by_poi where poi_name = 'XX'
order by hotel_id DESC, name ASC
Key說明
Key說明
Partition Key :決定了數據在Cassandra各個節點的是如何分區的。
Clustering Key : 用於在各個分區內的排序。
Primary Key : 主鍵,決定數據行的唯一性
Composite Key :只是一個多字段組合的概念, 決定數據唯一性
create table table01
key_part_one text ,
key_part_two int ,
key_part_one text ,
key_part_two int ,
key_clust_one text ,
key_clust_two int ,
key_clust_three uuid ,
data text ,
PRIMARY KEY (( key_part_one , key_part_two ), key_clust_one , key_clust_two , key_clust_three )
);
我們目標是設計出讓查詢可以在一個分區中完成的資料表, 若不行則盡可能最小化查詢所需接觸的分區.key_clust_two int ,
key_clust_three uuid ,
data text ,
PRIMARY KEY (( key_part_one , key_part_two ), key_clust_one , key_clust_two , key_clust_three )
);
跟關係型數據庫一樣,分區都是為了解決大數據量查詢的效率問題,所不同的是Cassandra的分區分佈在各個節點上。注意同一個分區會讓同一個id的數據在同一個物理節點上,這就造成一個問題,假如分區內的數據量過大的話,會造成Cassandra讀取負載的不均衡,可以用類似於table01的建表方式,多個字段共同組成一個partition key減小單個分區的大小,使各個分區能夠更均勻地分佈在節點上,從而實現負載均衡。
Cassandra之中的儲存,是2-level nested Map
Partition Key –> Custering Key –> Data
partition key: eq and in
clustering key: < <= = >= > in
設計優良範例:
- 盡量最小化查詢接觸到的分區數量, date如果加入分區鍵, 每個分區代表某飯店在特定時間是否可以預訂, 會造成太多小分區, 可多加入month欄位加入分區鍵, 雖然資料增加, 但會讓分區維持在恰當大小
- 依照查詢功能設計, 重複資料也無仿
create keyspace hotel with replication = {'class': 'SimpleStrategy', 'replication_factor': 3} create type hotel.address( street text, city text, state_or_province text, postal_code text, country text ); create table hotel.available_rooms_by_hotel_date( hotel_id text , month text, date date, room_number smaillint, is_available boolean, address frozen<address>, pois set<text>, PRIMARY KEY (( hotel_id, month ), date, room_number) ) with comment = 'Find available rooms by hotel / date '; create table hotel.hotel_by_poi( poi_name text , hotel_id text, name text, number list<text>, email set<text>, PRIMARY KEY (( poi_name ), hotel_id ) ) with comment = 'hotel by poi 景點旁的飯店'; create table poi_by_hotel( poi_name text , hotel_id text, description text, PRIMARY KEY (( hotel_id ), poi_name ) ) with comment = 'hotel by poi 飯店旁的景點'; |
常遇到Schema設計的問題
原始schema設計
上面主鍵為username. 如果我們需要根據username來查詢,會非常簡單與快捷
可是當我們需要根據email進行查詢的時候,當前的數據結構就不足以支撐我們的需求了。(如果你使用Allow Filtering做全表掃描,你可以跳過這一節了。。。)
原文來源:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/
方案1: 反範式(denormalization)
注意: 上面的主鍵已經變成了email。
這種方式其實還不錯,只是我們在維護起來會比較費勁:
- 需要維護多張表。
- CRUD都需要多次,在代碼層面也會比較麻煩
如果需要根據company查詢,又需要新增一張表,那麼代碼層面可能要全部的做一下調整
方案2: 二級索引(secondary index)
第二種方式是創建二級索引.:
在查詢的時候,就跟普通的MySQL查詢很類似了:
- 優點: 使用簡單、維護方便,代碼層面不需要做改動。
- 缺點:性能大打折扣
我們來看看二級索引的原理:二級索引,其實是自動創建了一個隱藏表,以上面的例子來說,會自動創建下面的隱藏表:
CREATE TABLE email_index(
email text,
username text,
PRIMARY KEY((email), username)
);
|
MV本身是一個視圖,概念與MySQL的視圖類似。但是如果我們使用過MySQL的視圖,我們知道MySQL之中的視圖是一個虛擬表。而在Cassandra之中,MV是真正的存儲著真實的數據,因此稱為Materialized(物化)
首先我們看看如何創建一個MV:
當原始表做了改動之後,視圖並不需要跟著改動。因為它的數據來源於select查詢語句。
如果我們需要按照username與company進行查詢,那麼我們還可以創建下面的MV:
commit log 與 memtable 與 SSTable
1.執行寫入會先寫到 commit log, commit log是失效後的還原機制 , 當關閉資料庫時下次會從log恢復到memtable
2.memtable(memory中), 若該row 存在cache中就更新快取
3.當到達一個閥值就將資料寫到 SSTable(硬碟), SSTable無法修改, 只能將新資料合併, 若資料無法寫入目標節點, 協調節點會為其保留Hint
Cassandra Node 包含下列:
Cassandra Daemon(JVM) : memtable . key Caches . Row Caches
Disk : commit log . memtable . Hint
讀取資料順序
Client 發出需求 >> 本地協調者 >> 透過分區器決定副本節點位置, 檢查回覆副本節點是否滿足一致性等級
Row caches >> Key caches >> memtables >> SSTables >>寫回row caches
一致性等級(consistency level)與複製因子(repli)
每個keyspace都須指定複製因子, 而一致性等級則在每個客戶端的查詢中設定.
複製因子代表寫入時希望持有資料副本的節點數目
一致性等級代表有多少節點回應讀取或寫入得請求才代表操作成功(ANY,許入任一節點, QUORUM 寫入過半副本) , 逾時rpc_time_out(預設10秒), 預設為ONE
寫入一致性等級 | ||||||||||||||
| ||||||||||||||
| ||||||||||||||
|
啟動Cassandra叢集
一個Cassandra服務稱為一個節點(node),而Cassandra叢集(Cluster)即是啟動個node一起工作,node啟動時會依據seeds的設置來找到其它node,node間的溝通稱為Gossip,為了讓結點可以透過網路進行Gossip,因此也必須設置bind的網路位置。依照前面的說明與設置bind位址預設為localhost,因此只能啟動single node(單一節點)的Cassandra,但如果要啟動多個節點,則必須修改「conf/cassandra.yaml」中的設置。
- seeds
- 堤供node啟動時能找到其他node的服務。適合設置較為穩定的node作為seeds
- listen_address
- listen to other C* node 的位址,即為Gossip使用的位址,可以用主機名稱或是用介面的IP位址,請勿使用0.0.0.0來bind所有的介面。
- rpc_address
- listen to client call的位址, cqlsh的入口,預設為listen_address安裝的IP即為Thrift,可以使用0.0.0.0來bind所有的介面。
啟動後可以用指令來查詢node的啟動狀態。
gossip協定
為了達成去中心化與分區容錯, 此協定讓每個節點持續追蹤叢集其他節點狀態 , 監控節點是否失效
分區器
決定資料分散到叢集哪個節點上, 每個row擁有一個分區鍵來決定所屬分區, 最後每筆row根據分區鍵計算出的印記職分配到所屬節點
Snitches告密
負責決定叢集中每個節點間的鄰近程度, 於挑挑選負責讀取或寫入工作的節點時會用到此資訊, 舉例來說, 一個query會先透過協調節點, 協調節點會去找副本節點, snitch協助辨識哪個副本節點回傳速度最快, 並選擇此節點讀取完整資料,
預設是SimpleSnitch, 不管網路拓譜架構, 無法應用於與跨資料中心部屬
預設是SimpleSnitch, 不管網路拓譜架構, 無法應用於與跨資料中心部屬
hinted handoff提示移交
有一個節點B的寫入需求但節點B失效, 建立一個提示, 當gossip偵測到B復活後, 移交給B
複製策略
SimpleStrategy 搭配 SimpleSnitch 不考慮資料中心與機架
NetworkTopologyStrategy 搭配 PropertyFileSnitch(機架感知, 明確設定節點位置, 速度快) or GossipingPropertyFileSnitch(透過gossip自己去找RACK與DC資訊) or 其他 , 允許每個DC指定複製因子, 總副本=all DC因子相加
修改複製因子 , 增加節點 node tool repair, 減少 node tool clean
DataStax Client
python : pip install cassandra-driver
C# : install-Package CassandraCSharpDriver
Ref:
- Jeff Carpenter & Eben Hewitt 著,許致軒譯 , Cassandra技術手冊 , O'REILLY
- 離世庭院
- /cassandra-guide,GitHub
- [Cassandra教程](十二)NoSQL也有視圖
沒有留言:
張貼留言