奇怪的 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
中为大多数连接和读取配置的超时时间不足为奇。
有几件事让我感到困惑:
- 查询仅在其中一个 Web 节点执行时超时 - 其中一个节点总是失败,其中一个节点总是成功。
- 当我从
cqlsh
运行 查询 returns 时立即查询(尽管当我从那里 运行 它时它似乎只命中一个节点)
- 发出的其他查询需要 2-3 分钟(比 10 秒的超时长很多),但根本不会超时
我无法跟踪 Java 中的查询,因为它超时了。跟踪 cqlsh
中的查询并没有提供太多见解。我不想更改 Cassandra 超时,因为这是生产系统,我想先用尽非侵入性选项。 Cassandra 节点都有足够的堆,它们的堆远未满,GC 时间似乎正常。
任何 ideas/directions 将不胜感激,我完全没有想法。 Cassandra 版本为 2.0.2,使用 com.datastax.cassandra:cassandra-driver-core:2.0.2
Java 客户端。
我注意到的几件事:
虽然您使用 time
作为集群键,但它并没有真正帮助您,因为您的查询不受分区键 (user_id
) 的限制。 Cassandra 仅按分区 内的聚类键 进行排序。所以现在您的查询正在拉回满足您的 WHERE 子句的第一行,按 user_id
的散列标记值排序。如果你真的有数千万行,那么我希望这个查询每次都从相同的user_id
(或相同的select少数)中提取数据。
"although it seems it only hits one node when I run it from there" 实际上,您的查询 应该当你 运行 它们时只命中一个节点。将网络流量引入查询会使它变得非常慢。我认为 cqlsh 中的默认一致性是 ONE。这就是 Carlo 的想法发挥作用的地方。
article_id
的基数是多少?请记住,二级索引在 "middle-of-the-road" 基数上效果最好。高(唯一)和低(布尔)都不好。
不应在(生产)应用程序端代码中使用 ALLOW FILTERING 子句。一如既往。如果你在这个 table 中有 5000 万行,那么 ALLOW FILTERING 首先将它们全部拉回来,然后根据你的 WHERE 子句修剪结果集。
建议:
Carlo 可能会建议尝试不同的(较低的)一致性级别。尝试在您的应用程序中设置 ONE
的一致性级别,看看是否有帮助。
要么执行 ALLOW FILTERING 查询,要么 二级索引查询。他们都很烂,但绝对不要一起做。我也不会使用。但如果我必须选择,我希望二级索引查询比允许过滤查询更糟糕。
为了在您描述的规模下充分解决这个问题,我会将数据复制到查询中 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
上聚类,有助于更快地返回最近的行。
我有一个包含三个或多或少默认配置的 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
中为大多数连接和读取配置的超时时间不足为奇。
有几件事让我感到困惑:
- 查询仅在其中一个 Web 节点执行时超时 - 其中一个节点总是失败,其中一个节点总是成功。
- 当我从
cqlsh
运行 查询 returns 时立即查询(尽管当我从那里 运行 它时它似乎只命中一个节点) - 发出的其他查询需要 2-3 分钟(比 10 秒的超时长很多),但根本不会超时
我无法跟踪 Java 中的查询,因为它超时了。跟踪 cqlsh
中的查询并没有提供太多见解。我不想更改 Cassandra 超时,因为这是生产系统,我想先用尽非侵入性选项。 Cassandra 节点都有足够的堆,它们的堆远未满,GC 时间似乎正常。
任何 ideas/directions 将不胜感激,我完全没有想法。 Cassandra 版本为 2.0.2,使用 com.datastax.cassandra:cassandra-driver-core:2.0.2
Java 客户端。
我注意到的几件事:
虽然您使用
time
作为集群键,但它并没有真正帮助您,因为您的查询不受分区键 (user_id
) 的限制。 Cassandra 仅按分区 内的聚类键 进行排序。所以现在您的查询正在拉回满足您的 WHERE 子句的第一行,按user_id
的散列标记值排序。如果你真的有数千万行,那么我希望这个查询每次都从相同的user_id
(或相同的select少数)中提取数据。"although it seems it only hits one node when I run it from there" 实际上,您的查询 应该当你 运行 它们时只命中一个节点。将网络流量引入查询会使它变得非常慢。我认为 cqlsh 中的默认一致性是 ONE。这就是 Carlo 的想法发挥作用的地方。
article_id
的基数是多少?请记住,二级索引在 "middle-of-the-road" 基数上效果最好。高(唯一)和低(布尔)都不好。不应在(生产)应用程序端代码中使用 ALLOW FILTERING 子句。一如既往。如果你在这个 table 中有 5000 万行,那么 ALLOW FILTERING 首先将它们全部拉回来,然后根据你的 WHERE 子句修剪结果集。
建议:
Carlo 可能会建议尝试不同的(较低的)一致性级别。尝试在您的应用程序中设置
ONE
的一致性级别,看看是否有帮助。要么执行 ALLOW FILTERING 查询,要么 二级索引查询。他们都很烂,但绝对不要一起做。我也不会使用。但如果我必须选择,我希望二级索引查询比允许过滤查询更糟糕。
为了在您描述的规模下充分解决这个问题,我会将数据复制到查询中 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
上聚类,有助于更快地返回最近的行。