【ClickHouse系列】分布式下的IN/JOIN及GLOBAL关键字

分布式下的IN/JOIN

如果是在单机情况下,涉及到IN/JOIN时是没有什么问题的,但是在分布式情况下就不一样了,ClickHouse是支持多分片多副本的,创建表也提供了友好的ON CLUSTER [name]的方式,所以就是建议使用者将数据进行分片处理增加读的效率,但也随之产生了单节点数据不完整的问题。如果SQL中涉及子查询就不得不有特殊的处理方式。

ClickHouse中的分布式子查询

在ClickHouse中为了方便做分布式查询,特意提供了Distributed表引擎,这个表引擎实际上是不存储数据的,单查这个表时,实际上是将SQL分发到该表引擎所关联的本地及远程节点执行,并把结果再汇总回来,类似一种分布式视图的效果。所以针对Distributed表的分布式查询情况做个汇总。

1.主子查询都查询本地表

例如:

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)

这种情况不会涉及网络数据传输,所有查询操作仅仅涉及执行节点,也仅仅会返回执行节点上的匹配数据。

2.主查询使用分布式表,子查询使用本地表

例如:

SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)

这种情况ck就会对SQL进行重写,将分布式表转换成本地表,并将SQL分发到存在local_table表的所有节点进行执行,再将结果进行汇总回传。其实可以理解成在每个存在local_table表的节点上都分别执行一遍第一种查询情况,最后进行合并回传。这种方式会因为数据不全导致结果错误,如果数据冗余,也会造成结果重复。

这种情况下,如果有n个节点,就会有n次查询操作。

3.主查询本地表,子查询使用使用分布式表

例如:

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

这种情况ck同样会对SQL进行重写,但此时不是将分布式表转换为本地表,而是直接分发这个SQL语句到存在local_table表的所有节点,在子查询是分布式表的情况下,每个接收到分发请求的节点先进行子查询,即到各个存在local_table表的节点执行SELECT UserID FROM local_table WHERE CounterID = 34再汇总回来,再和主查询语句继续执行查询,即每个被分发的节点都需要走一次这个流程,将最终的结果回传给初始执行节点,由初始执行节点将结果集合并,完成查询。

好像稍微有点绕,可以把这个SQL的主查询当作A,子查询当作B,假设有3个分片节点,那么需要先考虑第一个节点的执行情况,第一个节点为了能拿到子查询所有的数据,会先上自己上执行B,再去第二个节点执行B,再去第三个节点上执行B,然后再自己上执行A。同样,换到第二个节点,它同样会去一和三以及自己上执行B,再执行A。第三个几点以此类推,最终回传到初始执行节点进行汇总。

这种情况下,如果有n个节点,就会有n*n次查询操作。

4.主子查询都查询分布式表

例如:

SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

这种情况其实和第三种情况类似,但是主查询中也使用了分布式表,所以又多了一次查询汇总,所以如果有n个几点,那么会产生2*n*n次查询操作。

5.主子查询都查询分布式表,且使用GLOBAL关键字

例如:

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID GLOBAL IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

为什么要用GLOBAL关键字?

是因为上述的方法虽然能得到正确的查询结果,但是产生了查询放大,而且放大倍数非常大,为了解决这个问题,引入了GLOBAL关键字。

使用GLOBAL修饰后,会将子查询在初始执行节点进行查询汇总,存储为临时表,并在SQL分发时携带该临时表数据到各个节点进行查询,最终汇总结果到初始查询节点。

这种情况下,如果有n个节点,就会仅有2*n次查询操作。大限度的降低了查询放大问题。

补充:

  • 在使用GLOBAL关键字时,虽然最大限度的降低了查询放大,但是如果数据量过大,产生的临时表就会很大,也会受到网络稳定性和网络带宽的限制。ck在做JOIN时都是采用发送右表,所以ck在做分布式IN/JOIN时的效率不太好,所以在编写SQL时一定要多考虑这部分影响。
  • ck不支持数据的重分布,并不能将join key相同的数据落到同一节点,所以还不能支持将分布式join转换为本地join并汇总的方式。
ClickHouse 中创建分布式表可以通过以下步骤完成: 1. 首先,在 ClickHouse 集群中的每个节点上配置相同的集群名称。在每个节点的配置文件(通常是 `config.xml`)中,找到 `<remote_servers>` 节点,并确保它的内容在所有节点上是一样的。例如,你可以添加以下配置项: ``` <remote_servers> <cluster> <shard> <replica> <host>node1.example.com</host> <port>9000</port> </replica> <replica> <host>node2.example.com</host> <port>9000</port> </replica> ... </shard> ... </cluster> ... </remote_servers> ``` 其中 `node1.example.com`、`node2.example.com` 等是你集群中的每个节点的主机名和端口号。 2. 然后,在 ClickHouse 中创建一个分布式表。使用以下语法: ```sql CREATE TABLE distributed_table_name ( ... ) ENGINE = Distributed('cluster', 'database', 'table', hash_function); ``` 其中: - `distributed_table_name` 是你要创建的分布式表的名称; - `cluster` 是你在第一步中配置的集群名称; - `database` 是要分布式存储该表数据的数据库名称; - `table` 是要分布式存储该表数据的表名称; - `hash_function` 是用于确定数据行在集群中的哪个节点上存储的哈希函数。它可以是 ClickHouse 支持的任何哈希函数,如 `cityHash64`, `sipHash64`, `murmurHash64` 等。 3. 最后,将数据插入到分布式表中,ClickHouse 会自动将数据分发到集群中的不同节点上存储。 请注意,要成功创建和使用分布式表,你需要确保 ClickHouse 集群的配置正确,并且集群中的各个节点都正常运行和可访问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只努力的微服务

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值