在一致性 ONE 下读取查询期间 Cassandra 超时(需要 1 个响应,但只有 0 个副本响应)

Cassandra timeout during read query at consistency ONE (1 responses were required but only 0 replica responded)

我正在对具有 500000 行的 table 执行读取和更新查询,有时在处理大约 300000 行后出现错误,即使没有节点关闭。

Cassandra timeout during read query at consistency ONE (1 responses were required but only 0 replica responded)

基础架构详细信息:
有 5 个 Cassandra 节点、5 个 spark 和 3 个 Hadoop 节点,每个节点有 8 个内核和 28 GB 内存,Cassandra 复制因子3.

卡桑德拉 2.1.8.621 | DSE 4.7.1 |星火 1.2.1 | Hadoop 2.7.1.

卡桑德拉配置:

read_request_timeout_in_ms (ms): 10000
range_request_timeout_in_ms (ms): 10000
write_request_timeout_in_ms (ms): 5000
cas_contention_timeout_in_ms (ms): 1000 
truncate_request_timeout_in_ms (ms): 60000
request_timeout_in_ms (ms): 10000.

我也通过将 read_request_timeout_in_ms(毫秒)增加到 20,000 来尝试同样的工作,但没有帮助。

我正在对两个 table 进行查询。下面是 table 之一的创建语句:

创建Table:

CREATE TABLE section_ks.testproblem_section (
    problem_uuid text PRIMARY KEY,
    documentation_date timestamp,
    mapped_code_system text,
    mapped_problem_code text,
    mapped_problem_text text,
    mapped_problem_type_code text,
    mapped_problem_type_text text,
    negation_ind text,
    patient_id text,
    practice_uid text,
    problem_category text,
    problem_code text,
    problem_comment text,
    problem_health_status_code text,
    problem_health_status_text text,
    problem_onset_date timestamp,
    problem_resolution_date timestamp,
    problem_status_code text,
    problem_status_text text,
    problem_text text,
    problem_type_code text,
    problem_type_text text,
    target_site_code text,
    target_site_text text
    ) WITH bloom_filter_fp_chance = 0.01
    AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
    AND comment = ''
    AND compaction = {'class': 
    'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'}
    AND compression = {'sstable_compression': 
    'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

查询:

1) SELECT encounter_uuid, encounter_start_date FROM section_ks.encounters WHERE patient_id = '1234' AND encounter_start_date >= '" + formatted_documentation_date + "' ALLOW FILTERING;

2) UPDATE section_ks.encounters SET testproblem_uuid_set = testproblem_uuid_set + {'1256'} WHERE encounter_uuid = 'abcd345';

通常当您遇到超时错误时,这意味着您正在尝试做一些在 Cassandra 中无法很好扩展的事情。解决方法通常是修改您的架构。

我建议您在 运行查询时监视节点,看看是否可以发现问题区域。例如,您可以 运行 "watch -n 1 nodetool tpstats" 查看是否有任何队列正在备份或删除项目。查看其他监控建议 .

您的配置中可能有一个问题是您说您有五个 Cassandra 节点,但只有 3 个 spark worker(或者您是说每个 Cassandra 节点上有三个 spark worker?)您需要每个 Cassandra 节点上至少有一个 spark worker,以便将数据加载到 spark 是在每个节点本地完成的,而不是通过网络完成的。

如果没有看到您的架构和您正在 运行 查询,很难说出更多信息。你是从一个分区读取的吗?从单个分区读取时,我开始在 300,000 行附近出现超时错误。参见问题 here。到目前为止我发现的唯一解决方法是在我的分区键中使用客户端哈希将分区分成大约 100K 行的较小块。到目前为止,我还没有找到一种方法来告诉 Cassandra 不要对我预计需要很长时间的查询超时。

不要认为配置是根本原因,而是数据模型问题。

看到 section_ks.encounters table 的结构会很酷。

建议在设计 table(s) 结构之前仔细考虑 运行 期望的具体查询。

据我所知,这两个查询需要 section_ks.encounters 到 运行 的不同结构,性能良好。

让我们回顾每个提供的查询并尝试设计 tables:

第一个:

SELECT encounter_uuid, encounter_start_date FROM section_ks.encounters WHERE patient_id = '1234' AND encounter_start_date >= '" + formatted_documentation_date + "' ALLOW FILTERING;

  • 首先,如果 Cassandra 强制您添加 ALLOW FILTERING,那是非最佳查询或 table 结构的症状。
  • 第二点。首要的关键。关于 what are primary keys in Cassandra 如果 patient_id 列和 encounter_start_date 列将形成一个复合主键。 枚举 PRIMARY KEY() 语句中的列应与查询中的过滤顺序相对应。
  • 为什么在原始查询中强制允许过滤?通过分区键,Cassandra 知道数据位于哪个节点上。如果 patient_id 列不是分区键,Cassandra 必须扫描所有 5 个节点以查找请求的患者。当我们有大量跨节点数据时,这种全扫描通常会超时失败。

这里是一个 table 结构有效地适合给定查询的例子:

create table section_ks.encounters(
    patient_id bigint, 
    encounter_start_date timestamp, 
    encounter_uuid text,
    some_other_non_unique_column text,
    PRIMARY KEY (patient_id, encounter_start_date)
);
  • patient_id 列将是 "partition key"。负责跨 Cassandra 节点的数据分发。简而言之(省略复制功能):不同范围的患者将存储在不同的节点上。
  • encounter_start_date 列将是一个 "clustering key" 负责分区内的数据排序。

现在可以从查询中删除允许过滤:

SELECT encounter_uuid, encounter_start_date 
FROM section_ks.encounters 
WHERE patient_id = '1234' AND encounter_start_date >= '2017-08-19';

第二次查询:

UPDATE section_ks.encounters SET testproblem_uuid_set = testproblem_uuid_set + {'1256'} WHERE encounter_uuid = 'abcd345';

Table 结构应该类似于:

create table section_ks.encounters(
    encounter_uuid text, -- partition key
    patient_id bigint,
    testproblem_uuid_set text, 
    some_other_non_unique_column text,
    PRIMARY KEY (encounter_uuid)
);

如果我们明确希望仅通过 encounter_uuid 进行快速过滤,则应将其定义为分区键。

关于有效数据模型设计的好文章: