无法使用 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)

由于两个原因,主键定义会出现问题:

  1. stats_date_id 是一个TimeUUID。这对于数据分发来说很棒。但它的查询灵活性很差。事实上,您需要为特定分区的 return 数据提供准确的 TimeUUID 值。由于 TimeUUID 具有毫秒精度,因此您需要知道查询的确切时间,精确到毫秒。也许您有能力做到这一点,但通常这并不能构成一个好的分区键。

  2. 该分区 (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;

  1. 时间桶上的分区不如精确到 ms 的东西那么精确,但足够大以满足您通常的查询。
  2. 在第一个集群键上应用范围过滤器,一个分区内。