新设置 allow_asynchronous_read_from_io_pool_for_merge_tree 允许读取线程 (流) 的数量超过查询执行流水线其余部分的线程数。
通常,max_threads 设置会控制并行读取线程和并行查询处理线程的数量:

数据会按顺序从磁盘中逐列读取。
异步数据读取
新的设置 allow_asynchronous_read_from_io_pool_for_merge_tree 允许读取线程 (流) 的数量高于查询执行流水线其余部分中的线程数,从而加快低 CPU 的 ClickHouse Cloud 服务上的冷查询,并提升受 I/O 限制的查询性能。 启用该设置后,读取线程的数量由 max_streams_for_merge_tree_reading 设置控制:

数据会以异步方式从不同列并行读取。
请注意,还有一个 max_streams_to_max_threads_ratio 设置,用于配置读取线程 (流) 数量与查询执行流水线其余部分线程数之间的比率。不过,在基准测试中,它带来的提升不如 max_streams_for_merge_tree_reading 设置明显
optimize_read_in_order 呢?
借助 optimize_read_in_order 优化,如果查询的排序顺序与磁盘上数据的物理顺序一致,ClickHouse 就可以跳过在内存中对数据重新排序,但这要求按顺序读取数据 (而不是异步读取) :

optimize_read_in_order 优化的优先级高于异步读取
当 ClickHouse 判断可应用 optimize_read_in_order 优化 时,allow_asynchronous_read_from_io_pool_for_merge_tree 设置会被忽略 / 禁用。
演示上述所有内容的示例
-
检查 max_threads 的设置值 (默认值为 ClickHouse 在执行查询的节点上检测到的 CPU 核心数
SELECT getSetting('max_threads');
┌─getSetting('max_threads')─┐
│ 10 │
└───────────────────────────┘- 查看在读取和处理数据时使用默认线程数的查询管道
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid;
┌─explain──────────────────────┐
│ (Expression) │
│ ExpressionTransform × 10 │
│ (ReadFromMergeTree) │
│ MergeTreeThread × 10 0 → 1 │
└──────────────────────────────┘- 检查查询管道是否使用 60 个异步读取线程,以及查询执行流水线其余部分是否使用默认线程数
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
SETTINGS
allow_asynchronous_read_from_io_pool_for_merge_tree = 1,
max_streams_for_merge_tree_reading = 60;
┌─explain────────────────────────┐
│ (Expression) │
│ ExpressionTransform × 10 │
│ (ReadFromMergeTree) │
│ Resize 60 → 10 │
│ MergeTreeThread × 60 0 → 1 │
└────────────────────────────────┘- 检查查询管道,读取和处理数据均使用 20 个线程
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
SETTINGS
max_threads = 20;
┌─explain──────────────────────┐
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ MergeTreeThread × 20 0 → 1 │
└──────────────────────────────┘- 检查查询管道:其中 60 个线程用于异步读取,20 个线程用于其余查询执行流水线
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
SETTINGS
max_threads = 20,
allow_asynchronous_read_from_io_pool_for_merge_tree = 1,
max_streams_for_merge_tree_reading = 60;
┌─explain────────────────────────┐
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ Resize 60 → 20 │
│ MergeTreeThread × 60 0 → 1 │
└────────────────────────────────┘- 检查查询管道:在可应用
optimize_read_in_order 优化时,使用 60 个异步读取线程,其余查询执行流水线使用 20 个线程
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
ORDER BY postcode1, postcode2
SETTINGS
max_threads = 20,
allow_asynchronous_read_from_io_pool_for_merge_tree= 1,
max_streams_for_merge_tree_reading= 60;
┌─explain───────────────────────────┐
│ (Expression) │
│ ExpressionTransform │
│ (Sorting) │
│ MergingSortedTransform 20 → 1 │
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ MergeTreeInOrder × 20 0 → 1 │
└───────────────────────────────────┘
-- 注意:这等同于禁用 allow_asynchronous_read_from_io_pool_for_merge_tree
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
ORDER BY postcode1, postcode2
SETTINGS
max_threads = 20,
allow_asynchronous_read_from_io_pool_for_merge_tree = 0,
max_streams_for_merge_tree_reading = 0;
┌─explain───────────────────────────┐
│ (Expression) │
│ ExpressionTransform │
│ (Sorting) │
│ MergingSortedTransform 20 → 1 │
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ MergeTreeInOrder × 20 0 → 1 │
└───────────────────────────────────┘
-- 注意:可以通过禁用 optimize_read_in_order 来强制启用 allow_asynchronous_read_from_io_pool_for_merge_tree
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
ORDER BY
postcode1 ASC,
postcode2 ASC
SETTINGS
max_threads = 20,
allow_asynchronous_read_from_io_pool_for_merge_tree = 1,
max_streams_for_merge_tree_reading = 60,
optimize_read_in_order = 0;
┌─explain──────────────────────────────┐
│ (Expression) │
│ ExpressionTransform │
│ (Sorting) │
│ MergingSortedTransform 20 → 1 │
│ MergeSortingTransform × 20 │
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ Resize 60 → 20 │
│ MergeTreeThread × 60 0 → 1 │
└──────────────────────────────────────┘