优化在 WHERE 子句中使用算术运算的查询
Optimization of a query which uses arithmetic operations in WHERE clause
我需要检索过期日期为今天的记录。到期日期是使用其他两个字段(startDate
和 durationDays
)动态计算的:
SELECT * FROM subscription WHERE startDate + durationDays < currentDate()
为这两列加两个索引有意义吗?还是我应该考虑添加一个新列 expirationDate
并仅为它创建一个索引?
Cassandra 和关系数据库之间的主要区别之一是 table 的定义取决于将要使用的查询。数据检索方式 (WHERE statement
) 的条件应包含在主键中,因为它的性能优于 table.
上的索引
有多种关于读取路径的资源,以及主键与索引的怪癖,来自 Cassandra 峰会的 talk 可能会有用。
SELECT * FROM subscription WHERE startDate + durationDays < currentDate()
I'm wondering how does Cassandra handle such a filter as in my example? Does it make a full scan?
首先,您的问题基于 CQL 执行(日期)算术的能力。不能。
> SELECT * FROM subscription WHERE startDate + durationDays < currentDate();
SyntaxException: line 1:43 no viable alternative at input '+' (SELECT * FROM subscription WHERE [startDate] +...)
其次,currentDate()
函数在 Cassandra 3.11.4 中不存在。
> SELECT currentDate() FROM system.local;
InvalidRequest: Error from server: code=2200 [Invalid query] message="Unknown function 'currentdate'"
可以在 Cassandra 4.0 中工作,因为它还没有发布,你真的不应该使用它。
所以让我们假设您已经在 startDate
和 durationDays
上创建了您的二级索引,并且您只是查询它们,没有任何算术。
它是否执行完整的 table 扫描?
绝对。
原因是,仅在二级索引列上查询没有分区键。因此,它必须在所有节点的所有分区上搜索这些值。在大型集群中,您的查询可能会超时。
另外,当它找到匹配的数据时,它必须继续查询。因为这些值不是唯一的;完全有可能返回多个结果。 100% 正确的 Carlos 建议您根据要查询的内容重建 table。
建议:
- 尽量不要建有二级索引的table。一如既往
- 如果您必须使用二级索引构建 table,请尝试在
WHERE
子句中使用分区键以将查询隔离到单个节点。
- 任何对动态(计算)值的过滤都需要在应用程序端完成。
- 在您的情况下,创建一个名为
expirationDate
的列可能更有意义,在您的应用程序中计算日期,然后 INSERT
将该值输入您的 table。
- 您还需要遵循 "time bucket" 模式来处理时间序列数据(看起来就是这样)。假设
month
作为 "bucket" 工作(它可能适合您的用例,也可能不适合您的用例)。 PRIMARY KEY ((month),expirationDate,id)
会是一把好钥匙。这样,特定月份的所有订阅都存储在一起,按 expirationDate
聚类,最后 id
作为唯一性的决胜局。
我需要检索过期日期为今天的记录。到期日期是使用其他两个字段(startDate
和 durationDays
)动态计算的:
SELECT * FROM subscription WHERE startDate + durationDays < currentDate()
为这两列加两个索引有意义吗?还是我应该考虑添加一个新列 expirationDate
并仅为它创建一个索引?
Cassandra 和关系数据库之间的主要区别之一是 table 的定义取决于将要使用的查询。数据检索方式 (WHERE statement
) 的条件应包含在主键中,因为它的性能优于 table.
有多种关于读取路径的资源,以及主键与索引的怪癖,来自 Cassandra 峰会的 talk 可能会有用。
SELECT * FROM subscription WHERE startDate + durationDays < currentDate()
I'm wondering how does Cassandra handle such a filter as in my example? Does it make a full scan?
首先,您的问题基于 CQL 执行(日期)算术的能力。不能。
> SELECT * FROM subscription WHERE startDate + durationDays < currentDate();
SyntaxException: line 1:43 no viable alternative at input '+' (SELECT * FROM subscription WHERE [startDate] +...)
其次,currentDate()
函数在 Cassandra 3.11.4 中不存在。
> SELECT currentDate() FROM system.local;
InvalidRequest: Error from server: code=2200 [Invalid query] message="Unknown function 'currentdate'"
可以在 Cassandra 4.0 中工作,因为它还没有发布,你真的不应该使用它。
所以让我们假设您已经在 startDate
和 durationDays
上创建了您的二级索引,并且您只是查询它们,没有任何算术。
它是否执行完整的 table 扫描?
绝对。
原因是,仅在二级索引列上查询没有分区键。因此,它必须在所有节点的所有分区上搜索这些值。在大型集群中,您的查询可能会超时。
另外,当它找到匹配的数据时,它必须继续查询。因为这些值不是唯一的;完全有可能返回多个结果。 100% 正确的 Carlos 建议您根据要查询的内容重建 table。
建议:
- 尽量不要建有二级索引的table。一如既往
- 如果您必须使用二级索引构建 table,请尝试在
WHERE
子句中使用分区键以将查询隔离到单个节点。 - 任何对动态(计算)值的过滤都需要在应用程序端完成。
- 在您的情况下,创建一个名为
expirationDate
的列可能更有意义,在您的应用程序中计算日期,然后INSERT
将该值输入您的 table。 - 您还需要遵循 "time bucket" 模式来处理时间序列数据(看起来就是这样)。假设
month
作为 "bucket" 工作(它可能适合您的用例,也可能不适合您的用例)。PRIMARY KEY ((month),expirationDate,id)
会是一把好钥匙。这样,特定月份的所有订阅都存储在一起,按expirationDate
聚类,最后id
作为唯一性的决胜局。