cassandra數據做爲一個很是相似於關係型數據庫的nosql,其在數據表的設計上於關係型數據庫有着一些不一樣的地方。本文盡我的所理解的內容作簡單的剖析。html
Primary Keymysql
關係型數據的Primary Key主要是做爲數據的惟一性標識用的,對具體業務並無太大的幫助,好比大量使用的自增id做爲惟一主鍵,而這個id實際上並非業務的數據,只是做爲一個技術上保證惟一性標識的手段而已。sql
cassandra的Primary key也要保證數據的惟一性標識,可是其更大的做用是服務於查詢,也就是說cassandra的主鍵更可能是面向於業務來設計的,值得注意的是cassandra的表的primary key並不能動態改變,因此不少設計者犯得一個錯誤就是要查詢的字段並無在primary key中致使只能開啓全表掃描查詢,這是一種不可取的辦法,因此在設計之初就應該避免這種狀況。數據庫
複合主鍵nosql
cassandra主鍵包含了兩個部分,partition key和cluster key。在說明這兩個key以前須要簡單的介紹一下cassandra的一些基本特性,在關係數據庫的結構中當數據量增多的時候回致使單個表的數據查詢性能降低,設計者也缺乏好的策略去自動分割這些大量的數據。函數
cassandra則是採用partition來對於數據進行劃分保存,在良好設計的狀況下cassandra就會按照定義的策略自動對數據進行分區,而partition key就是cassandra分區的依據,經過一個一致性hash函數計算這個partition key的token進而直接找到其所在的分區。因此須要注意的是數據一旦寫入分區就不能update partition key了,當你能夠經過刪除而後再添加來實現。性能
表設計spa
在這裏以datastax的一個demo來展開說明。設計
race_year int, #比賽的年份htm
race_name text, #比賽的名稱
cyclist_name text, #選手名稱
rank int, #名次
若是是在mysql中這是一個很是簡單表,直接添加一個自增的主鍵id,而後把這四個字段加進去就能夠了。
在cassandra中這個表的設計則變得有點複雜了,下面咱們將參照不一樣的設計來分析所能應付的不一樣的業務。
在這以前咱們要首先確保數據的惟一性,在某一年的某一場比賽中的第幾名應該是能夠肯定到惟一的一條數據的。
假如表數據以下
CREATE TABLE rank_by_year_and_name (
race_year int,
race_name text,
cyclist_name text,
rank int,
PRIMARY KEY ((race_year),race_name,rank));
race_year做爲惟一的partition key,race_name和rank做爲cluster key,咱們能夠說這個表將會按照每個年份自動切分數據,而每個分區內都會按照比賽名和名稱作排序。
接下來咱們作一些簡單的練習。
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 2,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 1,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 2,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 1,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2019, '青海自行車賽', 1,'李明');
UPDATE rank_by_year_and_name SETcyclist_name = '博爾特'WHERErace_year = 223 ANDrace_name = '環法自行車賽' AND rank = 1 IF EXISTS;
select * from rank_by_year_and_name;
select * from rank_by_year_and_name where race_year=2018;
select * from rank_by_year_and_name where race_name='青海自行車賽';
select * from rank_by_year_and_name where rank=1;
select * from rank_by_year_and_name where race_name='青海自行車賽' and rank=1 and race_year=2018;
select * from rank_by_year_and_name where race_name='青海自行車賽' and rank=1 and race_year>=2018;
select * from rank_by_year_and_name where race_year=2018 and race_name='青海自行車賽' and rank>0;
select * from rank_by_year_and_name where race_year=2018 and rank>0;
select * from rank_by_year_and_name where race_name='青海自行車賽' and race_year in (2018,2019);
下面選擇幾個特別的查詢作特殊說明
第二句INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 2,'李明');
能夠看到當數據重複的時候cassandra不作任何提示.
UPDATE rank_by_year_and_name SETcyclist_name = '博爾特'WHERErace_year = 222 ANDrace_name = '環法自行車賽' ANDrank = 1;
使用if exists能夠防止update的時候變成插入
UPDATE rank_by_year_and_name SETcyclist_name = '博爾特'WHERErace_year = 223 ANDrace_name = '環法自行車賽' ANDrank = 1 IF EXISTS;
select * from rank_by_year_and_name where race_name='青海自行車賽';
select * from rank_by_year_and_name where rank=1;
這樣就意味着說咱們查詢只能在指定的partition key上操做,某種程度上這就是直接避免了關係型數據庫中的全表掃描。
select * from rank_by_year_and_name where race_name='青海自行車賽' and rank=1 and race_year>=2018;
select * from rank_by_year_and_name where race_name='青海自行車賽' and race_year in (2018,2019);
若是須要對多個分區進行操做,cassandra partition key不支持 >=這操做,儘管是int類型,而只能使用in查詢。緣由很簡單,partition key是用於一致性hash計算獲得token從而獲取分區的,而hash是分散的因此不會隨着partition key有大於小於的關係,可是in固然是能夠了。
select * from rank_by_year_and_name where race_year=2018 and rank>0;
select * from rank_by_year_and_name where race_year=2018 and race_name='青海自行車賽' ;
第二句能夠執行,第一句錯誤,緣由在cluster key是排序的,是有層級的,因此只能一層層的搜索。
總結
Cassandra在查詢的時候儘量避免不明確的查詢,查詢必須指定partition key,若是想要在多個partition key查詢,就要使用in來匹配多個,對於cluster key,層級關係也是很明確。Cassandra這樣設計目的在於減小大面積的不肯定的查詢,在tb甚至更多的數據中作模糊查詢可能會到來很是惡劣的效果。
很是差表設計
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, PRIMARY KEY ((race_year)));
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 2,'李明');INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行車賽', 1,'李明');
上面的表問題在沒法保證數據的惟一,數據會被覆蓋,因此存在明顯缺陷。
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, id int,PRIMARY KEY ((id)));
這個表幾乎是直接挪用關係型數據庫的表。
可是結果是因爲要查詢的字段都不在primary key,致使只能按照主鍵id查詢,幾乎等同於廢表一張。
接下來可能你會天真說,那好我把全部的字段都放到primary key裏。
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, id int,PRIMARY KEY ((id)),race_year,race_name,cyclist_name,rank);
可是彆着急當你須要查詢的須要制定一個partition key,而這個上面的表是一個id,當你按照某一個年份查詢的時候你只能使用in對全部的partition key作掃描。這很明顯又是一個很是差的選擇。
並且經過上面兩個反面的例子,你們應該更能理解cassandra和關係型數據庫在表設計上的差別,雖然不是十萬百千里那麼用,五萬四千裏仍是有的,若是你直接套路關係型數據庫的設計思路,那麼獲得的結果就是比用關係型數據庫還要差。