ClickHouse JOIN 报 Double-distributed:按分片数据选择 GLOBAL 或 local
两个 Distributed 表的 JOIN 或 IN 子查询触发 Code 288,需按数据分片选择 local 或 GLOBAL,并核对结果完整性、广播成本和实际执行位置。
ClickHouse 查询已经连上服务器,却在两个 Distributed 表之间执行 JOIN 或 IN 子查询时报 Double-distributed IN/JOIN subqueries is denied,常伴随 Code: 288 和 distributed_product_mode = 'deny'。这说明当前执行路径触发了分布式子查询限制;改客户端端口、增大连接池或重新创建账号不能解决它。
该设置针对特定的非 GLOBAL IN/JOIN 分布式子查询,外层与子查询所涉及的 Distributed 表都包含多个分片时才适用;不是所有 JOIN 都被禁止,也不等同于针对 remote 表函数的统一开关。旧版本与当前优化器可能选择不同的执行位置,下面按数据分布与实际查询路径判断。
先确定查询在哪些节点执行
核对外层与子查询表的引擎、Distributed 表对应的本地表、集群分片,以及两张表的分片规则。保存应用真正提交的 SQL 和查询设置,不要仅看 ORM 代码或连接配置中的默认值。
普通分布式 IN/JOIN 会把查询发送到远端节点,相关子查询在远端执行。子查询若又指向分布式表,就可能再次向整个集群发出请求。deny 的保护避免这类工作量在未评估的情况下展开。
给外层套一层 SELECT 后不再报错,可能与该版本改变了执行位置有关。它不是“括号修复了语法”,也不能保证升级后仍采用同一个计划。应对照目标版本的查询计划和查询日志,确认子查询的执行次数、结果传输与发起节点负担。
同一个关联键的数据共置,才考虑本地处理
如果能够证明外层每个关联键需要的右侧数据都在同一个分片上,可以使用对应本地表,或按当前规则让 distributed_product_mode='local' 将子查询映射到分片上的本地库表。
前提是映射准确、各分片结构一致,并且数据分布符合关联条件。“两张表都按某个键分片”还不够:关联键、分片表达式和实际数据都需要匹配。数据在不同分片时,只查右侧本地数据会漏掉本应匹配的行,即使查询已经不再报错。
改变为 local 之前,使用已知跨分片的数据检查结果,保留原 SQL 和设置。不要为了让错误消失,随意把 Distributed 表名替换成一个恰好存在的本地表。
右侧需要整个集群的集合,可评估 GLOBAL
GLOBAL 会先计算相关子查询结果,形成临时数据,再发送到远端节点参与处理。下面是 INNER JOIN 的结构示例,假定两个示例表都是已配置的 Distributed 表:
SELECT a.name
FROM events_all AS a
GLOBAL INNER JOIN
(
SELECT DISTINCT name
FROM eligible_all
WHERE enabled = 1
) AS b ON a.name = b.name;
这把右侧定义为“符合条件的 name 集合”,因此 DISTINCT 与业务含义一致。如果实际业务需要保留右侧重复行带来的 JOIN 倍数,不能为了减少传输量就擅自去重。
GLOBAL 不等于免费汇总。右侧结果会消耗计算、内存和网络资源,并发送到相关远端节点;大集合或跨机房集群尤其需要评估。应先限制到业务真正需要的列与范围,按目标版本核对 JOIN 或集合的资源限制,保留超限失败行为,不用允许部分结果来换一个“成功”响应。
这里的示例限于 INNER JOIN。其他 JOIN 类型在 GLOBAL 下计算哪一侧及保留哪一侧,要按对应版本规则核对,不能机械地把关键字插入所有 SQL。
allow 保留展开路径,不能当作默认修复
distributed_product_mode='allow' 允许原来的双分布式子查询执行,不自动把它变成 GLOBAL,也不会证明结果与成本符合预期。远端节点分别向集群执行子查询时,请求量可能随分片数量明显增长。
只有确认需要这条路径,并评估远端请求、内存、网络与查询时限后,才考虑限定在目标查询或受控会话中采用。不要修改全局用户配置来影响所有应用查询,也不要通过无限增大限制掩盖反复展开的工作量。
当前设置的 global 值会将相关 IN/JOIN 转为 GLOBAL;显式 SQL 与设置方式应由项目统一管理,避免用户配置、会话配置和应用 SQL 各自改变语义。连接池复用会话时,还应防止一次诊断设置影响随后无关的请求。
修复后同时验证结果与执行成本
用业务允许的只读数据范围比较关联结果,覆盖跨分片键、右侧重复键、无匹配行等情况。local 查询只在单个样本恰好全部共置时正确,不足以证明生产数据也满足前提。
再核对查询日志、资源使用和远端请求,确认实际路径与选择一致。错误消失但行数变化、请求激增或发起节点内存异常,说明还不能结束验收。
回退时恢复原 SQL、查询设置及相关会话配置;若调整过共享用户设置,要按保存的值恢复。上述查询示例只读,不需要重建表或重新分片数据来试错。真正的数据布局调整应单独规划迁移、验证和恢复步骤。


