A nova configuração allow_asynchronous_read_from_io_pool_for_merge_tree permite que o número de threads de leitura (streams) seja maior do que o número de threads no restante do pipeline de execução da consulta.
Normalmente, a configuração max_threads controla o número de threads de leitura paralelas e de processamento paralelo da consulta:

Os dados são lidos 'em ordem', coluna por coluna, do disco.
Leitura assíncrona de dados
A nova configuração allow_asynchronous_read_from_io_pool_for_merge_tree permite que o número de threads de leitura (streams) seja maior do que o número de threads no restante do pipeline de execução da consulta para acelerar consultas sem cache em serviços ClickHouse Cloud com pouca CPU e aumentar o desempenho de consultas limitadas por I/O. Quando a configuração está habilitada, a quantidade de threads de leitura é controlada pela configuração max_streams_for_merge_tree_reading:

Os dados são lidos de forma assíncrona, em paralelo, a partir de diferentes colunas.
Observe que também existe a configuração max_streams_to_max_threads_ratio para configurar a proporção entre o número de threads de leitura (streams) e o número de threads no restante do pipeline de execução da consulta. No entanto, em benchmarks, ela não ajudou tanto quanto a configuração max_streams_for_merge_tree_reading
E quanto ao optimize_read_in_order?
Com a otimização optimize_read_in_order, o ClickHouse pode evitar reordenar os dados na memória se a ordenação das consultas refletir a ordem física dos dados em disco, mas isso exige ler os dados em ordem (em contraste com a leitura assíncrona):

A otimização optimize_read_in_order tem precedência sobre a leitura assíncrona
Quando o ClickHouse detecta que otimização optimize_read_in_order pode ser aplicada, a configuração allow_asynchronous_read_from_io_pool_for_merge_tree será ignorada/desativada.
Exemplo que demonstra tudo o que foi explicado acima
-
Crie e carregue a tabela UK Property Price Paid
-
Verifique o valor configurado de max_threads (por padrão, a quantidade de núcleos de CPU que o ClickHouse vê no nó que executa a consulta
SELECT getSetting('max_threads');
┌─getSetting('max_threads')─┐
│ 10 │
└───────────────────────────┘- Verifique o pipeline da consulta com o número padrão de threads para leitura e processamento dos dados
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid;
┌─explain──────────────────────┐
│ (Expression) │
│ ExpressionTransform × 10 │
│ (ReadFromMergeTree) │
│ MergeTreeThread × 10 0 → 1 │
└──────────────────────────────┘- Verifique o pipeline da consulta com 60 threads de leitura assíncrona e o número padrão de threads para o restante do pipeline de execução da consulta
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 │
└────────────────────────────────┘- Verifique o pipeline da consulta com 20 threads, tanto para a leitura quanto para o processamento dos dados
EXPLAIN PIPELINE
SELECT *
FROM uk_price_paid
SETTINGS
max_threads = 20;
┌─explain──────────────────────┐
│ (Expression) │
│ ExpressionTransform × 20 │
│ (ReadFromMergeTree) │
│ MergeTreeThread × 20 0 → 1 │
└──────────────────────────────┘- Verifique o pipeline da consulta com 60 threads para leitura assíncrona e 20 threads para o restante do pipeline de execução da consulta
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 │
└────────────────────────────────┘- Verifique o pipeline da consulta com 60 threads de leitura assíncrona e 20 threads para o restante do pipeline de execução da consulta
quando a otimização
optimize_read_in_orderpuder ser aplicada
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 │
└───────────────────────────────────┘
-- observe que isso é equivalente a desabilitar 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 │
└───────────────────────────────────┘
-- observe que você pode forçar allow_asynchronous_read_from_io_pool_for_merge_tree desabilitando optimize_read_in_order
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 │
└──────────────────────────────────────┘