Cassandra 數據模型

Cassandra的數據模型相似於關係型數據庫的模型,且提供了與SQL語言很是相似的CQL語言進行操做。html

可是Cassandra的數據模型相似於多層鍵值對結構,與關係型數據庫存在巨大差異。java

本文基於: [cqlsh 5.0.1 | Cassandra 3.11.2 | CQL spec 3.4.4 | Native protocol v4]sql

目錄:數據庫

多層KV結構

Cassandra 的數據模型由 keyspace (相似關係型數據庫裏的database), column family(相似關係型數據庫裏的table), 主鍵(key)和列(column)組成。數據結構

對於一個 column family 不該該想象成關係型數據庫中的表, 而是一個多層的key-value結構:分佈式

Map<PartitionKey, SortedMap<ClusteringKey, Column>>

咱們使用CQL來描述:this

create table table1 (
    key1 int,
    key2 int,
    content text,
    PRIMARY KEY ((key1), key2)
);

在上述CQL建立的表(column family)中,key1是 partition key, 而 key2 是 clustering key, key1 和 key2 稱爲主鍵(PRIMARY KEY)spa

Cassandra支持更復雜的表結構:code

CREATE TABLE table2 (
    pkey1 int,
    pkey2 int,
    ckey1 int,
    ckey2 int,
    content text,
    PRIMARY KEY ((pkey1, pkey2), ckey1, ckey2)
);

此時的數據結構能夠描述爲:orm

Map<pkey1, Map<pkey2, SortedMap<ckey1, SortedMap<ckey2, content>>>>

做爲一個分佈式數據庫, Cassandra 根據 partition key 決定數據如何在集羣的各個節點上分區。clustering key 決定數據在分區內的排序。

查詢

下文中將以 table2 爲例介紹 cassadra 數據模型的特性。

從上文使用Map描述的表結構可知,咱們沒法根據非主鍵進行查詢(如table2中的data):

SELECT * FROM table2 WHERE content='a'; -- error
SELECT * FROM table2 WHERE pkey1 = 1 AND content='a'; -- error
SELECT * FROM table2 WHERE pkey1 = 1 AND pkey2 = 1; -- right

一般狀況下,在對形如((pkey1, pkey2), ckey1, ckey2)這樣的主鍵列進行查詢時須要注意:

  • partition key 僅支持精確查詢(=, in), 不能進行範圍查詢(>, <, >=, <=)。注: Cassandra 不支持 != 查詢。
  • 涉及多個 partition key 的查詢必須提供前置 partition key 的精確值。即若要查詢 pkey2 則必須提供 pkey1 的精確值。
  • 涉及 clustering key 的查詢,必須提供全部 partition key 的精確值
  • 涉及的 clustering key 不能跳躍,若要根據 ckey2 進行查詢則必須提供 ckey1 的精確值

(請不要記憶上述結論,根據Cassandra的內部數據結構很容易明白能夠進行什麼樣的查詢)

下面根據具體示例說明。

僅涉及partition key:

SELECT * FROM table2 WHERE pkey1 = 1; -- right
SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1; -- right
SELECT * FROM table2 WHERE pkey2=1; -- error

涉及一個 clustering key:

SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 AND ckey1>0; -- right
SELECT * FROM table2 WHERE pkey1=1 AND ckey1>0; -- error
SELECT * FROM table2 WHERE ckey1=1; -- error

涉及多個 clusterin key:

SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 AND ckey1=1 AND ckey2>0; -- right
SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 AND ckey2>0; -- error
SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 AND ckey1>0 AND ckey2=1; --error

排序

Cassandra 支持查詢結果按照 clustering key 進行排序,不過排序功能也很是有限:

SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 ORDER BY ckey1; -- right
SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 ORDER BY ckey1, ckey2; -- right
SELECT * FROM table2 WHERE pkey1=1 AND pkey2=1 ORDER BY ckey2; -- error

使用排序功能和涉及 clustering key 的查詢同樣, 必須提供全部 partition key 的精確值(= 或 in 運算符)。這是由於 Cassandra 僅支持單個節點上數據的排序。

涉及多個 clustering key 的排序必須按照 clustering key 的順序進行排序不能跳躍, 便可以ORDER BY ckey1, ckey2, 不能ORDER BY ckey2

默認狀況下只能進行升序排列,即ORDER BY ckey1 ASC, ORDER BY ckey2

這是由於Cassandra只能根據每一個節點上"SortedMap"固有的順序排列查詢結果,不過咱們能夠在建立表時自定義排序規則:

CREATE TABLE table2 (
    pkey1 int,
    pkey2 int,
    ckey1 int,
    ckey2 int,
    content text,
    PRIMARY KEY ((pkey1, pkey2), ckey1, ckey2)
) WITH CLUSTERING ORDER BY(ckey1 DESC, ckey2 ASC);

聚合

Cassandra 容許根據主鍵列定義的順序進行聚合:

SELECT count(*) FROM table2 GROUP BY pkey1; -- right
SELECT count(*) FROM table2 GROUP BY pkey1, pkey2; -- right
SELECT count(*) FROM table2 GROUP BY pkey1, pkey2, ckey1, ckey2; -- right
SELECT count(*) FROM table2 WHERE pkey1=1 GROUP BY pkey2; -- error

這種聚合能夠對多個節點上的數據進行聚合處理。

對於帶有WHERE條件的查詢, Cassandra 僅支持對單個節點上的數據進行聚合,就是說必須提供 partition key 的精確值才能進行聚合:

SELECT count(*) FROM table2 WHERE pkey1=1 AND pkey2=1 GROUP BY ckey1; -- right
SELECT count(*) FROM table2 WHERE pkey1=1 AND pkey2=1 GROUP BY ckey1, ckey2; -- right
SELECT count(*) FROM table2 WHERE pkey1=1 AND pkey2=1 GROUP BY ckey2; -- error
SELECT count(*) FROM table2 WHERE pkey1=1 GROUP BY pkey2; -- error

這一點與 Cassandra 查詢時的特徵是一致的。

Cassandra 支持 sum, min, max, count, distinct 等聚合功能, 不支持HAVING語句。

ALLOW FILTERING

上文咱們提到一些 Cassandra 不支持的查詢:

SELECT * FROM table2 WHERE ckey1=1;

能夠看到 Cassandra 的報錯信息:

Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING

對於 Cassandra 多層嵌套KV的數據結構來講,不可能經過 key 查找到相應數據,只能搜索全部的數據來完成此查詢。

對於一個有100萬條數據表而言,ckey1=1的記錄可能僅佔5%, 此時 Cassandra 仍能夠在可接受的時間內完成查詢。可是,Cassandra 並不瞭解此查詢須要搜索全部數據,所以須要操做者使用 ALLOW FILTERING 容許 Cassandra 掃描全部數據:

SELECT * FROM table2 WHERE ckey1=1 ALLOW FILTERING;

Cassandra 官方對於ALLOW FILTERING 進行了很是詳盡的說明, 能夠參考ALLOW FILTERING explained

做者提醒, ALLOW FILTERING 可能消耗大量時間和資源,請謹慎在生產環境下使用此功能。

次級索引

除了主鍵列以外咱們能夠爲 clustering key 和普通的值創建次級索引(secondary index)。

次級索引是一個另外的key-value映射, 能夠根據索引列直接查找到數據。

建立索引:

CREATE INDEX idx_ckey1 on table2(ckey1);
CREATE INDEX idx_content on table2(content);

使用索引進行查詢:

SELECT * FROM table2 WHERE ckey1=1; -- right
SELECT * FROM table2 WHERE content='a'; -- right
SELECT * FROM table2 WHERE ckey1>0; -- error
SELECT * FROM table2 WHERE pkey1=1 AND ckey1=1; --error

索引僅支持單獨、精確查詢, 不支持範圍查詢或者與主鍵(其它索引)聯合查詢。

請閱讀官方文檔When to use an index,瞭解索引的使用場景。

相關文章
相關標籤/搜索