深入理解CQL中的Where子句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解CQL中的Where子句相关的知识,希望对你有一定的参考价值。

参考技术A 虽然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的解释。

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

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

SELECT * FROM numberOfRequests

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

Clustering column的限制

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

clustering columns的无限制

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

CREATE TABLE numberOfRequests (

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

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

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

SELECT * FROM numberOfRequests

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

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

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

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

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

SELECT * FROM numberOfRequests

SELECT * FROM numberOfRequests

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

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

SELECT * FROM numberOfRequests

CONTAINS 和CONTAINS KEY 的使用限制

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

二级索引查询

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

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

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

CREATE TABLE contacts (

);

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

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子句对UPDATE和DELETE语句的限制

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

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

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

以上是关于深入理解CQL中的Where子句的主要内容,如果未能解决你的问题,请参考以下文章

深入理解C#泛型:new与where关键字全解析

一起去大厂系列深入理解MySQL中where 1 = 1的用处

一起去大厂系列深入理解MySQL中where 1 = 1的用处

(sql injection)sqli-lab 15: where 子句中的不可理解的行为

理解OVER子句

深入理解Spring Cloud一(4)Bean中的属性是如何刷新的?