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

Posted

技术标签:

【中文标题】奇怪的 Cassandra ReadTimeoutExceptions,取决于哪个客户端正在查询【英文标题】:Strange Cassandra ReadTimeoutExceptions, depending on which client is querying 【发布时间】:2015-05-19 16:30:49 【问题描述】:

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

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

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

表是用这个查询创建的:

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 中为大多数连接和读取配置的超时时间,这并不奇怪。

有几件事让我感到困惑:

查询仅在其中一个 Web 节点执行时超时 - 其中一个节点始终失败,其中一个节点始终成功。 当我从cqlsh 运行它时,查询会立即返回(尽管当我从那里运行它时它似乎只命中一个节点) 发出的其他查询需要 2-3 分钟(比 10 秒超时时间长得多),根本不会超时

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

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

【问题讨论】:

这张表有多少行? 在cqlsh中设置不同的一致性级别再试一次 @BryceAtNetwork23: 【参考方案1】:

我注意到的一些事情:

    当您使用 time 作为集群键时,它并不能真正帮助您,因为您的查询不受分区键 (user_id) 的限制。 Cassandra 仅通过分区内的集群键 进行排序。因此,现在您的查询正在拉回满足 WHERE 子句的第一行,按 user_id 的哈希标记值排序。如果您确实有数千万行,那么我希望此查询每次都能从相同的user_id(或相同的选择少数)中提取数据。

    “虽然我从那里运行它时它似乎只命中一个节点”实际上,您的查询应该只命中运行它们时的一个节点。将网络流量引入查询会使其非常慢。我认为 cqlsh 中的默认一致性是 ONE。这就是卡洛的想法发挥作用的地方。

    article_id 的基数是多少?请记住,二级索引在“中间”基数上效果最好。高(唯一)和低(布尔)都是不好的。

    不应在(生产)应用程序端代码中使用ALLOW FILTERING 子句。一如既往。如果此表中有 5000 万行,则 ALLOW FILTERING 首先将所有行拉回,然后根据您的 WHERE 子句修剪结果集。

建议:

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

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

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

    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);

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

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 上以 DESCending 顺序聚类,可以帮助您更快地返回最近的行。

【讨论】:

这很有见地,谢谢!该查询来自应用程序运行状况检查——我们期望连续的数据流,如果没有新行,则说明有问题(因此,只接收一个 user_id 的记录不是问题)。实际应用程序代码仅按 used_id 选择。无论如何,似乎没有什么可以解释为什么从一个盒子执行查询时会持续失败,而从另一个盒子执行时会持续成功。

以上是关于奇怪的 Cassandra ReadTimeoutExceptions,取决于哪个客户端正在查询的主要内容,如果未能解决你的问题,请参考以下文章

二级索引的Cassandra查询:ReadTimeout:code = 1200

查询现有数据时的Cassandra ReadTimeout

在Cassandra中使用用户定义函数时的Readtimeout

Cassandra优化之查询超时优化

Cassandra SELECT DISTINCT和超时问题

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