ClickHouse从分布式表的子查询中报表不存在问题看分布式表查询流程
ClickHouse从分布式表的子查询中报表不存在问题看分布式表查询流程
背景
有SQL如下执行报错(其中default.zkm表是分布式表,default.notdistribute表是普通的MergeTree引擎表):
CK01 :) select * from default.zkm where id in (select id from default.notdistribute); ...省略部分内容... Received exception from server (version 22.3.2): Code: 60. DB::Exception: Received from localhost:9000. DB::Exception: Received from dev-app77:9000. DB::Exception: Table default.notdistribute doesn't exist. (UNKNOWN_TABLE)
构造并说明
CREATE TABLE default.zkm_local ON CLUSTER zkm ( `id` String DEFAULT 'NULL' COMMENT '用户编号', `repo` String DEFAULT 'NULL' COMMENT '仓库编号' ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/zkm-{shard}/default/zkm_local', '{replica}') ORDER BY id; CREATE TABLE default.zkm ON CLUSTER zkm ( `id` String DEFAULT 'NULL' COMMENT '用户编号', `repo` String DEFAULT 'NULL' COMMENT '仓库编号' ) ENGINE = Distributed('zkm', 'default', 'zkm_local', hiveHash(id)); --default.notdistribute只有dev-app76(CK01)有。 CREATE TABLE default.notdistribute ( `id` String DEFAULT 'NULL' COMMENT '用户编号', `repo` String DEFAULT 'NULL' COMMENT '仓库编号' ) ENGINE = MergeTree ORDER BY id;
集群是一个两分片的架构:
CK01 :) select * from system.clusters where cluster='zkm'; SELECT * FROM system.clusters WHERE cluster = 'zkm' Query id: fc714b11-ef24-4200-947c-b92afaf6563f ┌─cluster─┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address──┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─slowdowns_count─┬─estimated_recovery_time─┐ │ zkm │ 1 │ 1 │ 1 │ dev-app76 │ 192.168.1.171 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │ │ zkm │ 2 │ 1 │ 1 │ dev-app77 │ 192.168.1.172 │ 9000 │ 0 │ default │ │ 0 │ 0 │ 0 │ └─────────┴───────────┴──────────────┴─────────────┴───────────┴───────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────┴─────────────────────────┘ 2 rows in set. Elapsed: 0.004 sec.
注意到在dev-app76(CK01)上执行报错SQL,错误信息是“Received from dev-app77:9000. DB::Exception: Table default.notdistribute doesn't exist.“。
dev-app77是集群中的另外个节点,这说明dev-app76的SQL被分解到dev-app77的SQL中也含有default.notdistribute表,所以报表不存在。
分布式查询和分布式子查询原理根据官网:中文 英文 或者《ClickHouse原理解析与应用实践》的P245-P250可知:
分布式查询SELECT * FROM default.zkm在请求节点被执行后会被转化为SELECT * FROM zkm_local后在发送到其他远端分片节点来执行,最终多个分片数据返回后在请求节点合并处理。
同样的,有分布式子查询如下:
SELECT id FROM zkm WHERE id in (SELECT ID FROM zkm_local);
该SQL会被分解为:
SELECT id FROM zkm_local WHERE id in (SELECT ID FROM local_table);
PS:在分布式子查询中这种分解原理可能会导致查询结果不准确(详见上边提到的官档或书籍),因此常用global in代替in解决此问题,不过gobal in也有需要注意的地方,不展开讲。
因此,回到一开始的SQL:
select * from default.zkm where id in (select id from default.notdistribute);
该SQL被分解为:
select * from default.zkm_local where id in (select id from default.notdistribute);
然后跑到dev-app77上边去执行,因此报表不存在。
解决方法
将SQL改为如下后可解决此问题:
select * from default.zkm where id global in (select id from default.notdistribute);
global in的原理不同于in,大致分为3个过程:
- 将IN子句单独拿出,由请求者服务器将运行子查询
- 结果将被放在RAM中的临时内存表中。 然后请求将被发送到每个远程服务器
- 将外层分布式表转为本地表后开始执行完成的SQL语句,IN子句直接使用临时内存表的数据
若子查询单独拿出后的SQL也是分布式查询,则按照分布式查询原理分解执行。
至此。