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     │         111 │ dev-app76 │ 192.168.1.17190001default │                  │            000 │
│ zkm     │         211 │ dev-app77 │ 192.168.1.17290000default │                  │            000 │
└─────────┴───────────┴──────────────┴─────────────┴───────────┴───────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────┴─────────────────────────┘

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个过程:

  1. 将IN子句单独拿出,由请求者服务器将运行子查询
  2. 结果将被放在RAM中的临时内存表中。 然后请求将被发送到每个远程服务器
  3. 将外层分布式表转为本地表后开始执行完成的SQL语句,IN子句直接使用临时内存表的数据

若子查询单独拿出后的SQL也是分布式查询,则按照分布式查询原理分解执行。

 

至此。

 

posted @ 2023-02-13 15:42  PiscesCanon  阅读(698)  评论(0编辑  收藏  举报