无法使用 timeuuid 作为主键执行时间序列查询
Unable to execute a timeseries query using a timeuuid as the primary key
我的目标是对给定时间范围内每个 DISTINCT provider_id 值的 messages_sent 和 emails_sent 求和 (fromDate < stats_date_id < toDate) , 但没有指定 provider_id。换句话说,我需要了解指定时间范围内的任何和所有提供者,并对它们的 messages_sent 和 emails_sent.
求和
我有一个使用 express-cassandra 模式(在 Node.js 中)的 Cassandra table,如下所示:
module.exports = {
fields: {
stats_provider_id: {
type: 'uuid',
default: {
'$db_function': 'uuid()'
}
},
stats_date_id: {
type: 'timeuuid',
default: {
'$db_function': 'now()'
}
},
provider_id: 'uuid',
provider_name: 'text',
messages_sent: 'int',
emails_sent: 'int'
},
key: [
[
'stats_date_id'
],
'created_at'
],
table_name: 'stats_provider',
options: {
timestamps: {
createdAt: 'created_at', // defaults to createdAt
updatedAt: 'updated_at' // defaults to updatedAt
}
}
}
要使其正常工作,我希望它像执行以下操作一样简单:
let query = {
stats_date_id: {
'$gt': db.models.minTimeuuid(fromDate),
'$lt': db.models.maxTimeuuid(toDate)
}
};
let selectQueries = [
'provider_name',
'provider_id',
'count(direct_sent) as direct_sent',
'count(messages_sent) as messages_sent',
'count(emails_sent) as emails_sent',
];
// Query stats_provider table
let providerData = await db.models.instance.StatsProvider.findAsync(query, {select: selectQueries});
然而,这抱怨需要过滤结果:
Error during find query on DB -> ResponseError: Cannot execute this query as it might involve data filtering and thus may have unpredictable performance
.
我猜您不能拥有主键并对其进行日期范围搜索?如果是这样,这种查询的正确方法是什么?
所以虽然没有使用过 Express-Cassandra,但我可以告诉你,运行对你的分区键进行范围查询是一个困难"no."原因是 Cassandra 不能' t 确定该查询的单个节点,因此它必须轮询每个节点。由于这本质上是跨多个节点对您的 table 进行全面扫描,因此它会抛出该错误以防止您 运行 执行错误查询。
但是,您 可以 运行 对聚类键进行范围查询,前提是您要过滤它之前的所有键。在你的情况下,如果我没看错的话,你的主键看起来像:
PRIMARY KEY (stats_date_id, created_at)
由于两个原因,主键定义会出现问题:
stats_date_id
是一个TimeUUID。这对于数据分发来说很棒。但它的查询灵活性很差。事实上,您需要为特定分区的 return 数据提供准确的 TimeUUID 值。由于 TimeUUID 具有毫秒精度,因此您需要知道查询的确切时间,精确到毫秒。也许您有能力做到这一点,但通常这并不能构成一个好的分区键。
该分区 (created_at
) 下的任何行都必须共享该确切时间,这通常会导致 partition:clustering 的基数比率很多 1:1键。
我对解决此问题的建议是在基数级别稍低的日期列上进行分区。想一想在特定时间范围内通常会保存多少提供者消息。还要选择不会将太多提供者消息存储在一起的东西,因为您不希望未绑定的分区增长(Cassandra 的硬限制是每个分区 20 亿个单元格)。
可能是这样的:PRIMARY KEY (week,created_at)
那么您的 CQL 查询可能类似于:
SELECT * FROM stats_provider
WHERE week='201909w1'
AND created_at > '20190901'
AND created_at < '20190905';
TL;DR;
- 时间桶上的分区不如精确到 ms 的东西那么精确,但足够大以满足您通常的查询。
- 在第一个集群键上应用范围过滤器,在一个分区内。
我的目标是对给定时间范围内每个 DISTINCT provider_id 值的 messages_sent 和 emails_sent 求和 (fromDate < stats_date_id < toDate) , 但没有指定 provider_id。换句话说,我需要了解指定时间范围内的任何和所有提供者,并对它们的 messages_sent 和 emails_sent.
求和我有一个使用 express-cassandra 模式(在 Node.js 中)的 Cassandra table,如下所示:
module.exports = {
fields: {
stats_provider_id: {
type: 'uuid',
default: {
'$db_function': 'uuid()'
}
},
stats_date_id: {
type: 'timeuuid',
default: {
'$db_function': 'now()'
}
},
provider_id: 'uuid',
provider_name: 'text',
messages_sent: 'int',
emails_sent: 'int'
},
key: [
[
'stats_date_id'
],
'created_at'
],
table_name: 'stats_provider',
options: {
timestamps: {
createdAt: 'created_at', // defaults to createdAt
updatedAt: 'updated_at' // defaults to updatedAt
}
}
}
要使其正常工作,我希望它像执行以下操作一样简单:
let query = {
stats_date_id: {
'$gt': db.models.minTimeuuid(fromDate),
'$lt': db.models.maxTimeuuid(toDate)
}
};
let selectQueries = [
'provider_name',
'provider_id',
'count(direct_sent) as direct_sent',
'count(messages_sent) as messages_sent',
'count(emails_sent) as emails_sent',
];
// Query stats_provider table
let providerData = await db.models.instance.StatsProvider.findAsync(query, {select: selectQueries});
然而,这抱怨需要过滤结果:
Error during find query on DB -> ResponseError: Cannot execute this query as it might involve data filtering and thus may have unpredictable performance
.
我猜您不能拥有主键并对其进行日期范围搜索?如果是这样,这种查询的正确方法是什么?
所以虽然没有使用过 Express-Cassandra,但我可以告诉你,运行对你的分区键进行范围查询是一个困难"no."原因是 Cassandra 不能' t 确定该查询的单个节点,因此它必须轮询每个节点。由于这本质上是跨多个节点对您的 table 进行全面扫描,因此它会抛出该错误以防止您 运行 执行错误查询。
但是,您 可以 运行 对聚类键进行范围查询,前提是您要过滤它之前的所有键。在你的情况下,如果我没看错的话,你的主键看起来像:
PRIMARY KEY (stats_date_id, created_at)
由于两个原因,主键定义会出现问题:
stats_date_id
是一个TimeUUID。这对于数据分发来说很棒。但它的查询灵活性很差。事实上,您需要为特定分区的 return 数据提供准确的 TimeUUID 值。由于 TimeUUID 具有毫秒精度,因此您需要知道查询的确切时间,精确到毫秒。也许您有能力做到这一点,但通常这并不能构成一个好的分区键。该分区 (
created_at
) 下的任何行都必须共享该确切时间,这通常会导致 partition:clustering 的基数比率很多 1:1键。
我对解决此问题的建议是在基数级别稍低的日期列上进行分区。想一想在特定时间范围内通常会保存多少提供者消息。还要选择不会将太多提供者消息存储在一起的东西,因为您不希望未绑定的分区增长(Cassandra 的硬限制是每个分区 20 亿个单元格)。
可能是这样的:PRIMARY KEY (week,created_at)
那么您的 CQL 查询可能类似于:
SELECT * FROM stats_provider
WHERE week='201909w1'
AND created_at > '20190901'
AND created_at < '20190905';
TL;DR;
- 时间桶上的分区不如精确到 ms 的东西那么精确,但足够大以满足您通常的查询。
- 在第一个集群键上应用范围过滤器,在一个分区内。