1. 程式人生 > >Cassandra 數據模型

Cassandra 數據模型

結果 sortedmap 範圍 from 鍵值對 tab 內部 信息 docs

Cassandra的數據模型類似於關系型數據庫的模型,且提供了與SQL語言非常類似的CQL語言進行操作。

但是Cassandra的數據模型類似於多層鍵值對結構,與關系型數據庫存在巨大差別。

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

目錄:

  • 多層KV結構
    • 查詢
    • 排序
    • 聚合
  • ALLOW FILTERING
  • 次級索引

多層KV結構

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

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

Map<PartitionKey, SortedMap<ClusteringKey, Column>>

我們使用CQL來描述:

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)

Cassandra支持更復雜的表結構:

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

此時的數據結構可以描述為:

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,了解索引的使用場景。

Cassandra 數據模型