join_algorithm
指定使用哪种 JOIN 算法。 可以指定多种算法,系统会根据具体查询的 kind/严格性 和表引擎选择可用的算法。 基于哈希的算法是否落盘不属于此选择的一部分:max_bytes_before_external_join / max_bytes_ratio_before_external_join 是所有这些算法的落盘阈值 (且其中任一项非零后,enable_adaptive_memory_spill_scheduler 可在内存压力下更早地将 join 落盘) ,而 max_rows_in_join / max_bytes_in_join 是所有这些算法的硬上限,除非 legacy_join_size_limits_trigger_spilling 将这两个上限重新变为磁盘落盘触发条件。您选择的值决定 join 如何落盘:grace_hash 从第一个块起就对右表进行分区,hash 和 parallel_hash 则先在内存中收集数据,并在超过阈值后切换。
大多数算法仅在被选用于查询时才会影响查询。不过,有些算法仅因被列出就会改变规划 — 即使它们只是最终未被选中的低优先级回退选项 — 因为决策会在选择算法之前作出。此类影响有两种:
- 连接键类型推断会变得更严格 (例如,merge join 无法连接不同类型的键,如
String和Nullable(String)) 。这可能会更改USING列的结果类型,并可能导致与Join引擎表的连接因TYPE_MISMATCH而失败。由full_sorting_merge和parallel_full_sorting_merge触发。 - 连接中保留侧的
ORDER BY ... LIMIT会执行显式排序,而不是按主键顺序读取,因为假定连接会破坏有序读取 (merge join 会插入自己的连接前排序;partial merge join 会重新排序左侧块;能够生成延迟块的 join 也不会传播有序读取) 。结果相同,但计划效率较低。由full_sorting_merge、parallel_full_sorting_merge、partial_merge、prefer_partial_merge、grace_hash和auto触发;非零的max_bytes_before_external_join/max_bytes_ratio_before_external_join也会触发。
hash 或其他算法运行,两者仍会生效。如果不希望如此,请勿在受影响查询的 join_algorithm 中列出上述算法。
可能的值:
- grace_hash
grace_hash 从第一个块起即采用外部算法:它会立即对右表进行分区,而 hash 和 parallel_hash 会先在内存中收集数据,仅在超过落盘阈值后才进行分区。如果您已知右侧数据无法容纳于内存中,并希望跳过内存阶段,请选择它。落盘阈值本身与所有哈希算法使用的阈值相同,即 max_bytes_before_external_join / max_bytes_ratio_before_external_join,并且两者之一必须非零,除非启用了 legacy_join_size_limits_trigger_spilling。如果没有阈值,grace_hash 会被跳过并使用列表中的下一个算法;如果它是唯一算法,则会被拒绝。
grace join 的第一阶段会读取右表,并根据键列的哈希值将其拆分为 N 个桶 (初始时,N 为 grace_hash_join_initial_buckets) 。这样可确保每个桶都能独立处理。第一个桶中的行会添加到内存中的哈希表,其他桶则保存到磁盘。如果哈希表增长超过溢写阈值,则会增加桶的数量,并重新分配每行所属的桶。任何不属于当前桶的行都会被刷新并重新分配。
支持 INNER/LEFT/RIGHT/FULL ALL/ANY JOIN。
- hash
JOIN ON 部分通过 OR 组合的多个连接键。
使用 hash 算法时,JOIN 的右侧部分会加载到 RAM 中。
- parallel_hash
hash join 的一种变体,会将数据拆分为多个桶,并同时构建多个哈希表而非一个哈希表,以加快此过程。
使用 parallel_hash 算法时,JOIN 的右侧部分会加载到 RAM 中。
- partial_merge
RIGHT JOIN 和 FULL JOIN 仅支持 ALL 严格性 (SEMI、ANTI、ANY 和 ASOF 不受支持)。
使用 partial_merge 算法时,ClickHouse 会对数据进行排序并将其转储到磁盘。ClickHouse 中的 partial_merge 算法与经典实现略有不同。首先,ClickHouse 会按连接键对右表数据进行分块排序,并为已排序的块创建 min-max 索引。然后,它会按 连接键 对左表的分片进行排序,并将其与右表连接。min-max 索引还用于跳过不需要的右表块。
- direct
direct (也称为 nested loop) 算法会使用左表中的行作为键,在右表中执行 lookup。
它适用于 Dictionary、EmbeddedRocksDB 和 MergeTree 表等特殊存储。
对于 MergeTree 表,该算法会将连接键过滤条件直接下推到存储层。如果该键可以利用表的主键索引进行 lookup,这种方式会更高效;否则,它会针对左表的每个块对右表执行全表扫描。
支持 INNER 和 LEFT join,并且仅支持不带其他条件的单列等值连接键。
- auto
auto 时,会先尝试 hash join;如果超出内存限制,则会动态切换到其他算法。
- full_sorting_merge
- ie_join
ON 部分包含两个参与连接表表达式之间不等比较 (<、<=、>、>=) 的 JOIN。支持 ALL INNER/LEFT/RIGHT/FULL JOIN 和 SEMI/ANTI LEFT/RIGHT JOIN。
在列表中的位置决定优先级:若列在其他算法之后,仅当其他算法不适用时才使用 IEJoin (即 ON 部分没有等值条件);若列在首位,只要 ON 部分包含两个不等式条件便会使用它。对于 ALL INNER JOIN,其余条件 (包括等值条件) 会作为过滤器应用于连接结果;对于其他 kind,则会在 operator 内部作为影响匹配的残余条件进行评估。当 ON 部分包含超过两个符合条件的不等式条件时,算法使用的两个条件会根据列 min/max 统计信息估算的选择性来选定 (参见 Column statistics 中的 basic 类型);当估算不可用时 (没有统计信息,或禁用了 use_statistics),则使用语法顺序中的前两个条件。如果列表中没有 ie_join,仅含不等条件的 INNER JOIN 会作为带过滤器的 CROSS JOIN 执行,其他 kind 不受支持。
两个输入都会在连接前累积到内存中:max_rows_in_join 和 max_bytes_in_join 限制两侧累积输入的总量 (而非仅限右侧) ,溢出时的处理方式由 join_overflow_mode 设置;operator 在累积输入上构建的排序索引不计入该限制。join operator 本身以单线程运行;仅输入在 join 前的排序会并行执行。
- parallel_full_sorting_merge
full_sorting_merge 相同,但哈希兼容的等值 join 会根据连接键的哈希分片为相互独立的每分片 merge join,并行运行 (最多使用 max_threads) ,而非作为单个 merge join 运行。这在使用所有线程的同时保留了 merge join 较低的流式内存使用量,且结果不保证有序。
仅对键类型的哈希与 merge join 比较结果一致的普通等值 join 应用基于连接键的哈希分片,并且仅在两侧均未排序时应用。以下情况会跳过:
ASOFjoin,以及浮点数 /JSON/Object/Dynamic键类型:其哈希与 merge join 比较结果不一致,因此相等的键可能落在不同分片中。- 已排序的侧 (按顺序读取的 MergeTree,或任何预排序输入):保序地将数据分散到每分片 merge join 中可能会导致管道死锁。因此会保留按顺序读取及其
read_in_order_use_virtual_row优化。 - 当 initiator 构建分布式查询计划 (
make_distributed_plan) 时,因为分散排序无法序列化以供远程执行。本地单片段计划和每个 worker 的片段会在禁用该设置后重新优化,因此仍可进行分片。
full_sorting_merge 运行;当启用 query_plan_join_shard_by_pk_ranges 时,按顺序读取的 MergeTree 侧仍可在数据源处按主键 ranges 进行分片 (这些 ranges 按与 join 相同的比较方式排序,因此相等的键会保持在一起)。
- prefer_partial_merge
partial_merge join,否则使用 hash。已弃用,等同于 partial_merge,hash。
- default (已弃用)
direct,hash,即依次尝试使用 direct join 和 hash join。
join_any_take_last_row
当右表中某个键对应多于一条匹配行时,此设置会更改具有ANY 严格性 的 JOIN 操作行为。
此设置适用于
Join 引擎表以及基于哈希的 JOIN 算法。如果 join 是并行构建的,行的顺序可能是非确定性的。这意味着对于 ANY JOIN 查询,join_any_take_last_row = 1 可能会返回非确定性的行。- 0 — 如果右表中某个键对应多于一条匹配行,则仅关联找到的第一行。
- 1 — 如果右表中某个键对应多于一条匹配行,则仅关联找到的最后一行。
join_default_strictness
设置 JOIN 子句的默认严格性。 可能的值:ALL— 如果右表有多行匹配,ClickHouse 会根据匹配行创建笛卡尔积。这是标准 SQL 中JOIN的常规行为。ANY— 如果右表有多行匹配,则只会连接找到的第一行。如果右表只有一行匹配,则ANY和ALL的结果相同。ASOF— 用于连接匹配关系不确定的序列。Empty string— 如果查询中未指定ALL或ANY,ClickHouse 会抛出异常。
join_on_disk_max_files_to_merge
限制 MergeJoin 操作在磁盘上执行时,并行排序可使用的文件数量。 该设置的值越大,占用的 RAM 越多,对磁盘 I/O 的需求越少。 可能的值:- 任意大于等于 2 的正整数。
join_output_by_rowlist_perkey_rows_threshold
在 hash join 中,用于判断是否按行列表输出的右表每个键平均行数下限。join_overflow_mode
定义了当 join 达到以下任一限制时,ClickHouse 将执行的操作: 所有基于哈希的join_algorithm
取值都会遵循此设置,包括那些会落盘的算法:达到限制时会停止查询,而不是触发落盘。例外是
legacy_join_size_limits_trigger_spilling:启用它后,join 中已经在磁盘上运行的部分会继续落盘,而不按此设置执行。
ie_join 同样遵循此设置,作用于它从两侧累积的输入。partial_merge 则仍然通过切换策略来处理这些限制——请参见
join_algorithm。
可能值:
THROW— ClickHouse 抛出异常并停止查询。BREAK— ClickHouse 停止查询,但不抛出异常。
THROW。
另请参见