虽然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<text, text>,
emails set<text>
);
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 'Benjamin@oops.com';
二级索引过滤器
二级索引查询允许您使用过滤在非索引列上使用=,>,> =,<=和<,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 在最后一个分区键列上的限制