使用Cassandra CQL3从具有复合主键的表中选择行键

4

我正在使用使用CQL3的官方Java驱动程序,版本为Cassandra 1.2.7。

假设有一个表通过以下命令创建:

CREATE TABLE foo ( 
    row int, 
    column int, 
    txt text, 
    PRIMARY KEY (row, column)
);

接下来我想执行与SELECT DISTINCT row FROM foo等效的操作。

据我理解,由于Cassandra实现了复合主键,因此在其数据模型内高效地执行此查询应该是可能的,因为它只会查询“原始”表。

我搜索了CQL文档,但没有找到任何可以执行此操作的选项。

我的备选方案是创建一个单独的表,类似于:

CREATE TABLE foo_rows (
    row int,
    PRIMARY KEY (row)
);

但这需要保持两者同步的麻烦 - 对于foo中的任何写操作都要写入foo_rows(还会影响性能)。

那么有没有办法查询不同的行(分区)键?

3个回答

7

首先,我会告诉你一个不好的做法。如果你插入以下这些行:

insert into foo (row,column,txt) values (1,1,'First Insert');
insert into foo (row,column,txt) values (1,2,'Second Insert');
insert into foo (row,column,txt) values (2,1,'First Insert');
insert into foo (row,column,txt) values (2,2,'Second Insert');

做一个

'select row from foo;' 

将会给你以下内容:

 row
-----
   1
   1
   2
   2

由于它显示了所有可能的行和列的组合,因此不是唯一的。要查询获取一个行值,可以添加一个列值:

select row from foo where column = 1;

但是你会收到这个警告:
Bad Request: 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

好的。那么,有了这个:

select row from foo where column = 1 ALLOW FILTERING;

 row
-----
   1
   2

很好,这正是我想要的。但我们不能忽视那个警告。如果你只有少量的行,比如10000行,那么这个查询不会对性能造成很大影响。但是如果我有10亿行呢?根据节点数和副本因子的不同,性能将受到严重影响。首先,查询必须扫描表中的每一行(称为全表扫描),然后过滤结果集中的唯一值。在某些情况下,此查询可能会超时。考虑到这一点,这可能不是你要找的。

你提到你担心在多个表中插入数据会影响性能。在数据建模方面,多表插入是一种完全有效的技术。Cassandra可以处理大量写入操作。至于在同步方面是否会有问题,我不知道你的具体应用程序,但我可以给出一些一般性的建议。

如果你需要进行唯一扫描,你需要考虑分区列。这就是我们所谓的索引或查询表。在任何Cassandra数据模型中,考虑到应用程序查询至关重要。例如,如果我正在使用IP地址作为行键,我可能会创建以下内容以按顺序扫描所有IP地址。

CREATE TABLE ip_addresses (
 first_quad int,
 last_quads ascii,
 PRIMARY KEY (first_quad, last_quads)
);

现在,要在我的192.x.x.x地址空间中插入一些行:
insert into ip_addresses (first_quad,last_quads) VALUES (192,'000000001');
insert into ip_addresses (first_quad,last_quads) VALUES (192,'000000002');
insert into ip_addresses (first_quad,last_quads) VALUES (192,'000001001');
insert into ip_addresses (first_quad,last_quads) VALUES (192,'000001255');

为了在192个空间中获取不同的行,我这样做:
SELECT * FROM ip_addresses WHERE first_quad = 192;

 first_quad | last_quads
------------+------------
        192 |  000000001
        192 |  000000002
        192 |  000001001
        192 |  000001255

要获取每一个地址,您只需要迭代从0到255的每一个可能的行键。在我的示例中,我希望应用程序请求特定的范围以保持性能。您的应用程序可能有不同的需求,但希望您可以看到这里的模式。

不错的技巧。但我的问题是列不会是异构的 - 我不能确定所有行中都有一个列“1”。而且我有点想从你的IP示例中反过来做。假设您还有第一组8和10的行。那么我想编写一个查询,返回(8,10,192)。但我无法知道存储了哪些IP。 - edofic

4
根据文档,从CQL版本3.11开始,Cassandra支持DISTINCT修饰符。因此,您现在可以编写:

SELECT DISTINCT row FROM foo

0

@edofic

分区行键用作唯一索引,以区分存储引擎中的不同行,因此,行键本质上始终是不同的。您不需要在SELECT子句中使用DISTINCT。

示例

 INSERT INTO foo(row,column,txt) VALUES (1,1,'1-1');
 INSERT INTO foo(row,column,txt) VALUES (2,1,'2-1');
 INSERT INTO foo(row,column,txt) VALUES (1,2,'1-2');

那么

SELECT row FROM foo

将返回2个值:1和2

以下是Cassandra中的持久化方式

+----------+-------------------+------------------+
| 行键 | 列1/值 | 列2/值 |
+----------+-------------------+------------------+
|       1     |          1/'1'         |          2/'2'       |
|       2     |          1/'1'         |                       |
+----------+-------------------+------------------+


1
这是我使用以下插入语句后执行 SELECT row FROM foo; 返回的结果:

cqlsh:test> SELECT row FROM foo; ` row

1 1 2 `这也是我首先提出这个问题的原因(我理解如何持久化具有复合键的表)。
- edofic
@edofic,你除了使用“DISTINCT”关键字之外,找到了解决方案吗? - FelikZ
1
@FelikZ 不,"DISTINCT" 正好符合我的需求。 - edofic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接