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

相关文章
相关标签/搜索