深入理解CQL中的Where子句

虽然CQL和SQL他们之间有很多的不同,但是他们也有很多相类似的语法。造成这些差异的原因主要来自于Cassandra处理分布式数据并旨在防止低效查询的事实。

其中CQL与SQL一个很大不同的地方在于他们的where子句。本文的目的就是描述CQL WHERE子句所支持的内容以及与普通SQL不同的原因。

主键列

在Cassandra数据中,主键列有两种数据类型组成而且他们有着特殊的意义:分区键列(the partition key columns )和集群列(the clustering columns)。他们两组合在一起

就确定了你每行的主键(相当于mysql的主键一样)。

分区键(partition key)列是主键的第一部分,其作用是将数据均匀地分布在集群中。行将依据分区键(partition key)的hash值分布在集群周围(注:说白了就是每行的数据放在集群的哪台机器是根据partition key进行hash计算来决定的)。

聚簇列(the clustering columns)通常用于聚集分区的数据,从而可以非常有效地检索行。

由于它们扮演的角色不同,分区键,clustering和普通列在WHERE子句使用中有着不同的限制。而且,这些限制条件根据查询类型而不同:比如SELECT,UPDATE或DELETE。

SELECT语句的WHERE子句限制

分区键key的限制

分区键列仅支持两个运算符:=和IN

IN的使用限制

在2.2版本之前,IN只能应用到分区键的最后一个列。所以,比如,如果你的表是下面这样的话:

CREATE TABLE numberOfRequests (

cluster text,

date text,

time text,

numberOfRequests int,

PRIMARY KEY ((cluster, date), time)

)

在2.1版本中,您只能在date这列上使用IN运算符。在2.2版本中,你可以在分区键列中的任何列中使用IN运算符.

最后,你的查询会像这样子:

SELECT * FROM numberOfRequests

WHERE cluster IN ('cluster1', 'cluster2')

AND date = '2015-05-06'

AND time >= '12:00'

AND time <= '14:00';

这个查询从2.2版本开始是正确的,但是在之前的版本是错误的。

这个更新使CQL更统一了,但是你还是应该小心在分区键列使用IN运算符的限制。 Ryan Svihla的好文章会给你一个清晰的解释,告诉你为什么要尽量避免它们。

2.2版本引入的另一个变化是操作结果不会按IN子句指定的分区键顺序返回。从2.2版本开始,操作结果以列类型的自然顺序返回而且重复值被忽略。

无限制的分区键列

Cassandra要求您要么限制所有分区键列要么一点都不限制,除非你的查询可以使用二级索引。

这意味着这个查询像这样子的:

SELECT * FROM numberOfRequests WHERE cluster='cluster1' AND time ='12:00';

这个查询将会拒绝因为date这列是不受限制的。

之所以这样,是因为Cassandra需要所有的分区键列才能够计算散列,以便它能够定位包含该分区的节点。

如果没有在分区键上指定限制条件,但在集群键上指定了某些限制条件,则Cassandra将要求ALLOW FILTERING被添加到查询中。有关ALLOW FILTERING的更多信息,您应该查看ALLOW FILTERING的解释。

, >=, <= and < operators

Cassandra distributes the partition accross the nodes using the selected partitioner.由于只有ByteOrderedPartitioner保持数据的有序分布,所以Cassandra不直接在分区键上支持>,> =,<=和<运算符。

然而,它允许您通过使用标记功能(token function)在分区键上使用>,>,<=和<运算符。

SELECT * FROM numberOfRequests

WHERE token(cluster, date) > token('cluster1', '2015-06-03')

AND token(cluster, date) <= token('cluster1', '2015-06-05')

AND time = '12:00';

如果使用ByteOrderedPartitioner,则可以在多个分区上执行一些范围查询。你应该小心,不建议使用ByteOrderedPartitioner,因为它可能会导致群集不平衡。

Clustering column的限制

Clustering column支持单列的=,IN,>,> =,<=,<,CONTAINS和CONTAINS KEY运算符以及多列的=,IN,>,> =,<=和<运算符。

clustering columns的无限制

clustering columns的作用是对分区内的数据进行群集。如果你有下面的表格:

CREATE TABLE numberOfRequests (

cluster text,

date text,

datacenter text,

hour int,

minute int,

numberOfRequests int,

PRIMARY KEY ((cluster, date), datacenter, hour, minute))

数据将按以下方式存储在每个分区中:

{datacenter: US_WEST_COAST {hour: 0 {minute: 0 {numberOfRequests: 130}} {minute: 1 {numberOfRequests: 125}} … {minute: 59 {numberOfRequests: 97}}} {hour: 1 {minute: 0 …

您可以看到,为了在没有二级索引的情况下以有效的方式检索数据,你需要知道你选择的所有集群键列。

所以,如果你执行下面语句:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND hour = 14

AND minute = 00;

Cassandra将高效的找到上面所查询的数据,但是如果你执行的语句是下面这样的:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND hour = 14

AND minute = 0;

Cassandra会拒绝上面这条语句的查询,因为它必须扫描整个分区才能找到请求的数据,效率不高(注:其实就是clustering key只能从左向右加条件且中间不能断,你可以只用给datacenter = 'US_WEST_COAST' 条件,hour和minute不给,但是你不能使用了minute字段但是没hour字段的查询)。

IN在Clustering column中的限制

在2.2版本之前,只有最后一个集群列(clustering columns)允许对集群列进行IN限制。在2.2中,IN限制可以用于任何列,下面的查询将起作用:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND hour IN (14, 15)

AND minute = 0;

通过使用多列IN限制(multi-column IN restriction),可以在2.2版本之前检索相同的一组数据:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND (hour, minute) IN ((14, 0), (15, 0));

在2.2中,多列IN限制可以应用于任何一组集群列。

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND (datacentre, hour) IN (('US_WEST_COAST', 14), (‘US_EAST_COAST’, 17))

AND minute = 0;

在2.2之前,多列IN限制只能应用于最后一组被限制的集群列。结果,以前的查询在2.1中是无效的。但是下面的查询是完全有效的。

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND (hour) IN ((14), (15));

, >=, <= and < restrictions(>, >=, <= and < 的使用限制)

单列在执行范围查询的时候只能出现在查询条件的最后一栏。

因此,下面的查询是正确的:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND hour= 12

AND minute >= 0 AND minute <= 30;

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND hour >= 12;

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter > 'US';

但是下面这条语句是不正确的:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND hour >= 12 AND minute = 0;

多列范围查询的时候最一组clustering columns的限制。

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WESTCOAST'

AND (hour, minute) >= (12, 0) AND (hour, minute) <= (14, 0)

如果你的查询是多列分片且后面一组是第一组列的子集,那么第二组的查询的列必须以第一组的第一列打头,如下面的列子:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacenter = 'US_WEST_COAST'

AND (hour, minute) >= (12, 30) AND (hour) < (14)

这条语句是正确的,但是下面这条是错误的:

SELECT * FROM numberOfRequests

WHERE cluster = ‘cluster1’

AND date = ‘2015-06-05’

AND datacentre = 'US_WEST_COAST'

AND (hour, minute) >= (12, 0)

AND (minute) <= (45)

CONTAINS 和CONTAINS KEY 的使用限制

CONTAINS和CONTAINS KEY限制只能在查询使用二级索引时用于集合。

二级索引查询

对二级索引的直接查询只支持=,CONTAINS或CONTAINS KEY。

CONTAINS只能用于集合类型。 CONTAINS KEY只能用于map集合且map的key是建立了index的。

例如,你如果有这样的table:

CREATE TABLE contacts (

id int PRIMARY KEY,

firstName text,

lastName text,

phones map,

emails set

);

CREATE INDEX ON contacts (firstName);

CREATE INDEX ON contacts (keys(phones)); // Using the keys function to index the map keys

CREATE INDEX ON contacts (emails);

接下来的查询是生效的:

SELECT * FROM contacts WHERE firstname = 'Benjamin';

SELECT * FROM contacts WHERE phones CONTAINS KEY 'office';

SELECT * FROM contacts WHERE emails CONTAINS '[email protected]';

二级索引过滤器

二级索引查询允许您使用过滤在非索引列上使用=,>,> =,<=和<,CONTAINS和CONTAINS KEY来查询返回的结果。

因此,下面的查询是有效的,只要指定了ALLOW FILTERING:

SELECT * FROM contacts

WHERE firstname = 'Benjamin'

AND lastname = 'Lerer'

ALLOW FILTERING;

SELECT * FROM contacts

WHERE phones CONTAINS KEY 'office'

AND phones CONTAINS '0000.0000.0000'

ALLOW FILTERING;

你应该谨慎的使用filtering,因为这操作代价很高。

分区键上的二级索引限制

当Cassandra必须执行二级索引查询时,它将联系所有节点以检查位于每个节点上的二级索引的部分。如果所有分区键组件都受到限制,则Cassandra将使用该信息只查询包含指定分区键的节点,这将使查询更高效。

对于二级索引查询,分区键列上只支持=操作。

Clustering column restrictions and Secondary indices

对于每个索引值,Cassandra存储了整个主键(分区键列+集群列)的每一行包含值。当执行索引查询时,Casssandra将从索引中检索包含该值的行的主键。然后它将从表中检索行并执行所需的任何过滤。

如果第一个Clustering column已经被限制,Cassandra将对索引返回的主键执行一个过滤,使得过滤效率更高。

对于这种类型的过滤,Cassandra的 Clustering column将接受以下操作:=,IN,>,> =,<=和<。

所以,如果我们将以下二级索引添加到numberOfRequests表中:

CREATE INDEX ON numberOfRequests (minute);

他下面的查询是完全有效的:

SELECT * FROM numberOfRequests

WHERE cluster = 'cluster1'

AND date = '2015-06-05'

AND datacenter IN ('US_WEST_COAST', 'US_EAST_COAST')

AND minute = 0

ALLOW FILTERING;

WHERE子句对UPDATE和DELETE语句的限制

在UPDATE和DELETE语句中,所有主键列都必须受到限制,唯一允许的限制是:

1. 单列情况 = 可以作用在任何分区键或集群列上

2.单列IN 在最后一个分区键列上的限制

你可能感兴趣的:(深入理解CQL中的Where子句)