奇怪的 Cassandra ReadTimeoutExceptions,取决于哪个客户端正在查询

Strange Cassandra ReadTimeoutExceptions, depending on which client is querying

我有一个包含三个或多或少默认配置的 Cassandra 节点的集群。最重要的是,我有一个网络层,由两个用于负载平衡的节点组成,两个网络节点一直在查询 Cassandra。一段时间后,随着存储在 Cassandra 中的数据变得非常重要,一个且只有一个 Web 节点开始在特定查询上获得 ReadTimeoutException。 Web 节点在各个方面都是相同的。

查询很简单(?是日期的占位符,一般是当前时刻的前几分钟):

SELECT * FROM table WHERE time > ? LIMIT 1 ALLOW FILTERING;

table 是使用此查询创建的:

CREATE TABLE table (
    user_id varchar,
    article_id varchar,
    time timestamp,
    PRIMARY KEY (user_id, time));
CREATE INDEX articles_idx ON table(article_id);

超时时,客户端会等待 10 多秒,这是 cassandra.yaml 中为大多数连接和读取配置的超时时间不足为奇。

有几件事让我感到困惑:

我无法跟踪 Java 中的查询,因为它超时了。跟踪 cqlsh 中的查询并没有提供太多见解。我不想更改 Cassandra 超时,因为这是生产系统,我想先用尽非侵入性选项。 Cassandra 节点都有足够的堆,它们的堆远未满,GC 时间似乎正常。

任何 ideas/directions 将不胜感激,我完全没有想法。 Cassandra 版本为 2.0.2,使用 com.datastax.cassandra:cassandra-driver-core:2.0.2 Java 客户端。

我注意到的几件事:

  1. 虽然您使用 time 作为集群键,但它并没有真正帮助您,因为您的查询不受分区键 (user_id) 的限制。 Cassandra 仅按分区 内的聚类键 进行排序。所以现在您的查询正在拉回满足您的 WHERE 子句的第一行,按 user_id 的散列标记值排序。如果你真的有数千万行,那么我希望这个查询每次都从相同的user_id(或相同的select少数)中提取数据。

  2. "although it seems it only hits one node when I run it from there" 实际上,您的查询 应该当你 运行 它们时只命中一个节点。将网络流量引入查询会使它变得非常慢。我认为 cqlsh 中的默认一致性是 ONE。这就是 Carlo 的想法发挥作用的地方。

  3. article_id 的基数是多少?请记住,二级索引在 "middle-of-the-road" 基数上效果最好。高(唯一)和低(布尔)都不好。

  4. 不应在(生产)应用程序端代码中使用 ALLOW FILTERING 子句。一如既往。如果你在这个 table 中有 5000 万行,那么 ALLOW FILTERING 首先将它们全部拉回来,然后根据你的 WHERE 子句修剪结果集。

建议:

  1. Carlo 可能会建议尝试不同的(较低的)一致性级别。尝试在您的应用程序中设置 ONE 的一致性级别,看看是否有帮助。

  2. 要么执行 ALLOW FILTERING 查询,要么 二级索引查询。他们都很烂,但绝对不要一起做。我也不会使用。但如果我必须选择,我希望二级索引查询比允许过滤查询更糟糕。

  3. 为了在您描述的规模下充分解决这个问题,我会将数据复制到查询中 table。看起来您关心的是组织时间敏感的数据,以及获取最新的数据。像这样的查询 table 应该可以做到:

    CREATE TABLE tablebydaybucket ( user_id varchar, article_id varchar, time timestamp, day_bucket varchar, PRIMARY KEY (day_bucket , time)) WITH CLUSTERING ORDER BY (time DESC);

用您的数据填充此 table,然后此查询将起作用:

SELECT * FROM tablebydaybucket 
WHERE day_bucket='20150519' AND time > '2015-05-19 15:38:49-0500' LIMIT 1;

这将按 day_bucket 对数据进行分区,并按 time 对数据进行聚类。这样,您就不需要 ALLOW FILTERING 或二级索引。此外,您的查询保证只命中一个节点,Cassandra 将不必拉回您的所有行并在事后应用您的 WHERE 子句。并按降序在 time 上聚类,有助于更快地返回最近的行。