clickhouse分布式查询降级为本地查询

在基于 clickhouse 做类数仓建模时通常的做法是在本地创建物化视图,然后使用分布式表做代理对外提供服务。我们知道 clickhouse 对于 DQL 内部实现了分布式,而对于 DDL 则需要我们自动实现比如:

drop table table_name on cluster cluster_name;

来实现分布式 DDL,但对于

select count() from distributed_table_name;

此类分布式表的查询会自动执行分布式查询并在查询入口节点汇总最终数据,即 MPP 架构。

image-20230727094657201

在 clickhouse 中本地表通常以 local 结尾,而代理它们的分布式表通常以 all 结尾(只是规范)

但在一些特殊情况下,上述的分布式查询会降级为本地查询,此时对分布式表的查询会随机路由到一张代理的本地表中导致该 SQL 的返回结果在反复横跳即为诡异

image-20230727094948720

一、问题复现

1.1 知识点复习

这里先复习一下本文所涉及到的知识点或概念:副本和分片

在 clickhouse 中用户可以将所有节点按照自己的需求组成不同应用场景的集群,可以将所有节点组成一个单一集群也可以划分成多个小集群。副本和分片的区别在于:

  1. 从功能层面说:副本是防止数据丢失,增加数据冗余;分片是实现数据的水平切分,提高查询效率
  2. 从数据层面说:副本之间的数据是完全相同的;分片之间的数据是完全不同的

clickhouse 目前支持副本的表引擎只有 MergeTree Family,其格式为 Replicated*MergeTree,同时副本表需要依赖 zookeeper 实现个节点数据同步的协同工作,这里配置集群、zookeeper以及复制表是如何工作的就不做过多介绍。集群的信息通过下面的命令查看

select * from system.clusters;

结果如下

image-20230727102133634

创建复制表需要在原 MergeTree 表的基础上额外指定 zookeeper 路径(分片信息)和副本信息

engine = ReplicatedMergeTree(path:String, replica:String [, columns:any])

path一致表示它们是同一个分片,后面的 replica 标记为不同的副本,通常 replica 填写本机 ip 或主机名

下面创建一个 3 分片 2 副本的 MergeTree 表作为本次问题复现的 ODS 表

create table event_local on cluster cluster
(ldtime Datetime,ip     IPv4
) engine = ReplicatedMergeTree('/clickhouse/tables/default/{shard}/event_local', '{replica}')order by ldtimepartition by toYYYYMMDD(ldtime);

说明:

{shard}{replica}是 clickhouse 的宏,在配置文件中指定的可以通过下面的 sql 查看

select *
from system.macros;

结果如下

image-20230727105208951

同时 path 也存在一定的约束,通常是/clickhouse/tables/数据库名称/分片编号/表名

on cluster cluster是标记该 DDL 为分布式 DDL 且最后的 cluster 为集群名称上面截图所示,因此该 DDL 语句会被发送到 cluster 的集群中所有节点去执行,省去了我们手动去各个节点执行的步骤。同时该分布式 DDL 也会展示所有节点的响应,执行 SQL 的入口节点会等待所有节点创建完将信息返回用户(默认最大等待时间为 180s,超过后会转入后台进行)

当然也可以使用 remote 查询所有节点宏信息

select *
from remote('chi-settings-01-cluster-0-0', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard')
union all
select *
from remote('chi-settings-01-cluster-0-1', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard')
union all
select *
from remote('chi-settings-01-cluster-1-0', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard')
union all
select *
from remote('chi-settings-01-cluster-1-1', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard')
union all
select *
from remote('chi-settings-01-cluster-2-0', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard')
union all
select *
from remote('chi-settings-01-cluster-2-1', 'system', 'macros', 'username', 'password')
where macro in ('replica', 'shard');

随机向三个分片中写入一定量的数据,可以观察副本数据的同步

# chi-settings-01-cluster-0-0
insert into event_local
values (toDateTime('2023-07-01 00:00:00'), '192.168.0.1'),(toDateTime('2023-07-01 00:00:00'), '192.168.0.1'),(toDateTime('2023-07-01 00:00:00'), '192.168.0.1'),(toDateTime('2023-07-02 00:00:00'), '192.168.0.2');
# chi-settings-01-cluster-1-1
insert into event_local
values (toDateTime('2023-07-03 00:00:00'), '192.168.0.3'),(toDateTime('2023-07-03 00:00:00'), '192.168.0.3'),(toDateTime('2023-07-04 00:00:00'), '192.168.0.4');
# chi-settings-01-cluster-2-0
insert into event_local
values (toDateTime('2023-07-05 00:00:00'), '192.168.0.5'),(toDateTime('2023-07-06 00:00:00'), '192.168.0.6');

根据分片的特性 event 全量数据应该是各个分片的 compact 因此我们需要一张分布式表来代理所有的副本分片表

create table event_all
(ldtime Datetime,ip     IPv4
) engine = Distributed('{cluster}', 'default', 'event_local', rand());

rand() 为分片健,当数据通过分布式表写入时,会根据分片健将数据写入不同的分片中。分片健要求是一个整型数值,可以是表字段或返回整型的函数。

注:生产中分布式表通常只做查请求,不做写请求。因为如果对分布式表执行写请求每条数据都需要在 clickhouse 中计算所属分片效率不高,建议的做法是业务测做”分库分表“将数据直接写入对应的本地表不让 clickhouse 来做自动分片。

1.2 复现问题

对 event_all 查询结果如下

select * from event_all order by ldtime;2023-07-01 00:00:00,192.168.0.1
2023-07-01 00:00:00,192.168.0.1
2023-07-01 00:00:00,192.168.0.1
2023-07-02 00:00:00,192.168.0.2
2023-07-03 00:00:00,192.168.0.3
2023-07-03 00:00:00,192.168.0.3
2023-07-04 00:00:00,192.168.0.4
2023-07-05 00:00:00,192.168.0.5
2023-07-06 00:00:00,192.168.0.6

该 SQL 是执行了分布式查询,对于副本表 clickhouse 会按照一定策略读取其中一个副本读取数据,具体的副本表读写流程不是这篇文章重点。下面开始复现问题

简化一下需求,实时统计每个 ip 出现的个数。正常数仓可能会这么做

create materialized view mv_ip_countengine = SummingMergeTree(num)order by ippopulate
as
select ip, count() as num
from event_all
group by ip;192.168.0.1,3
192.168.0.2,1
192.168.0.3,2
192.168.0.4,1
192.168.0.5,1
192.168.0.6,1

这样的结果是没有问题的,但是生产中不是所有的需求都可以这么做。因为之所以创建分布式表是因为数据量很大,需要做分片。那么对于处理后的数据量依然很大的需求我们就不能通过查询分布式表来创建物化视图,因为这样做该物化视图所有的数据都会存储在该节点上

为了解决这个问题就需要各个节点去统计自己存储在本地的数据,然后再创建一张分布式表对外提供统一的服务,sql 如下

create materialized view mv_ip_count_local on cluster clusterengine = SummingMergeTree(num)order by ippopulate
as
select ip, count() as num
from event_local
group by ip;create table mv_ip_count_all
(ip  IPv4,num UInt64
) engine = Distributed('{cluster}', 'default', 'mv_ip_count_local', rand());

当我们用 mv_ip_count_all 进行二次聚合时

select sum(num) from mv_ip_count_all;

其值在 4、3、2 间反复横跳,正确答案应该是 9。这就很纳闷了

二、问题解决

这种情况定位出来就是触发了本地查询,反复横跳的数据是各个节点本地查询结果。产生这个问题的原因是下面这个 sql

create materialized view mv_ip_count_local on cluster clusterengine = SummingMergeTree(num)order by ippopulate
as
select ip, count() as num
from event_local
group by ip;

首先这个 sql 会被分布式执行,但是 event_local 它是一个副本表,该 sql 执行结束后每个节点都会有一张毫无关联的 SummingMergeTree 表,此时我们又创建了分布式表来代理,造成的后果就是分布式表的数据量暴增 n 倍,n 为副本个数。

当我们基于分布式表做二次聚合时,clickhouse 或许也发现了这个问题,如果按照正常的 mpp 架构来执行这个 sql 得到的结果就是正确个数的 n 倍,显然这样错误的结果是 clickhouse 无法接受的,因此它将分布式 sql 降级为本地 sql 如文章开头的图二流程。

或许 clickhouse 觉得这样的错误结果比 mpp 流程得到的结果要更容易接受吧,虽然这两个结果都存在问题

分析到这里解决方案也就浮出水面了,就是让 SummingMergeTree 保持和 event_local 一样的副本分片关系即可,即创建具有副本功能的 SummingMergeTree 表

create materialized view mv_ip_count_localon cluster clusterengine = ReplicatedSummingMergeTree('/clickhouse/tables/default/{shard}/mv_ip_count_local', '{replica}', num)order by ip
as
select ip, count() as num
from event_local
group by ip;

分布式表正常创建即可,最终问题解决。

三、总结

出现这个问题本质上是对 clickhouse 的副本分片表、分布式表不熟练,同时 clickhouse 在赋予用户极高自由度的同时也给用户带来了很多心智负担。clickhouse 分布式表还存在很多错误的使用方式,例如基于分布式表做聚合或连接操作时存在大坑,这点后面有时间可以单独出一篇博客来说明。虽然在使用过程中产生过很多匪夷所思的结果,但当分析完原因并成功解决后又会觉得 clickhouse 这么做很合理,它依然是一款极其优秀且强大的 olap 数据库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11218.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP 协议【传输层协议】

文章目录 1. 简介1.1 TCP 协议是什么1.2 TCP 协议的作用1.3 什么是“面向连接” 2. 简述 TCP2.1 封装和解包2.2 TCP 报文格式2.3 什么是“面向字节流”2.4 通过 ACK 机制实现一定可靠性 3. 详述 TCP3.1 基本认识TCP 报头格式16 位源/目标端口号32 位序列号*32 位确认应答号4 位…

HCIA实验四

一.实验要求: 1、R4为ISP,其上只能配置IP地址;R4与其他所有直连设备间均使用共有IP; 2、R3 - R5/6/7为MGRE环境,R3为中心站点; 3、整个网络配置OSPF环境,IP基于172.16.0.0/16网段划分&#x…

element中el-input组件限制输入条件(数字、特殊字符)

1、只能输入纯数字 <el-input v-model"aaa" type"text" input"(v)>(aaav.replace(/[^\d]/g,))" /> 2、只能输入纯数字和小数&#xff08;比如&#xff1a;6.66&#xff09; <el-input v-model"aaa" type"text&quo…

idea下tomcat运行乱码问题解决方法

idea虚拟机选项添加-Dfile.encodingUTF-8

青枫壁纸小程序V1.4.0(后端SpringBoot)

引言 那么距离上次的更新已经过去了5个多月&#xff0c;期间因为忙着毕业设计的原因&#xff0c;更新的速度变缓了许多。所以&#xff0c;这次的更新无论是界面UI、用户功能、后台功能都有了非常大的区别。希望这次更新可以给用户带来更加好的使用体验 因为热爱&#xff0c;更…

[OnWork.Tools]系列 02-安装

下载地址 百度网盘 历史版本连接各种版本都有,请下载版本号最高的版本 链接&#xff1a;https://pan.baidu.com/s/1aOT0oUhiRO_L8sBCGomXdQ?pwdn159提取码&#xff1a;n159 个人链接 http://on8.top:5000/share.cgi?ssiddb2012fa6b224cd1b7f87ff5f5214910 软件安装 双…

com.android.ide.common.signing.KeytoolException:

签名没问题但是提示Execution failed for task :app:packageDebug. > A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable > com.android.ide.common.signing.KeytoolException: Failed to read ke…

【iOS】自定义字体

文章目录 前言一、下载字体二、添加字体三、检查字体四、使用字体 前言 在设计App的过程中我们常常会想办法去让我们的界面变得美观&#xff0c;使用好看的字体是我们美化界面的一个方法。接下来笔者将会讲解App中添加自定义字体 一、下载字体 我们要使用自定义字体&#x…

2023天津Java培训学校分享!Java培训班

近年来&#xff0c;各类培训机构如雨后春笋般涌现&#xff0c;其中&#xff0c;Java培训机构可谓是风头正盛&#xff0c;许多想踏入这行的小伙伴选择这个方式来学习Java技能&#xff0c;今天我们一起来讨论一下&#xff1a;学Java有门槛吗&#xff0c;Java培训的好处&#xff0…

Windows使用Notepad++编辑Linux服务器的文件

&#x1f680; Windows使用Notepad编辑Linux服务器的文件 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介…

适用于虚拟环境的免费企业备份软件

多年来&#xff0c;许多行业严重依赖物理服务器提供计算资源——你可以想象到巨大的服务器机房和笨重的服务器的场景。 然而&#xff0c;随着业务快速增长&#xff0c;许多组织发现物理服务器已经无法有效利用计算资源。因此&#xff0c;为了节省成本&#xff0c;引入了虚拟服…

ChatGPT | 分割Word文字及表格,优化文本分析

知识库读取Word内容时&#xff0c;由于embedding切片操作&#xff0c;可能会出现表格被分割成多个切片的情况。这种切片方式可能导致“列名栏”和“内容栏”之间的Y轴关系链断裂&#xff0c;从而无法准确地确定每一列的数据对应关系&#xff0c;从而使得无法准确知道每一列的数…

本地部署 Stable Diffusion XL 1.0 Gradio Demo WebUI

StableDiffusion XL 1.0 Gradio Demo WebUI 0. 先展示几张 StableDiffusion XL 生成的图片1. 什么是 Stable Diffusion XL Gradio Demo WebUI2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 安装 Stable Diffusion XL Gradio Demo WebUI6. 启动 Stable Diffusion XL Gradi…

单相锁相环原理与代码实战解释

单相锁相环程序原理如下图所示 单相锁相环原理 锁相环&#xff08;PLL&#xff09;是一种常用于同步、解调和信号处理等领域的电路或数字算法&#xff0c;其主要作用是将一个输入信号的相位与频率与参考信号进行精确的匹配。这里我们来简单解释一下单相锁相环的原理和分析。 …

在线平面设计工具盘点,提升效率首选

在移动应用程序或网页UI设计项目中&#xff0c;在线平面图工具是必不可少的。市场上的在线平面图工具绘制软件丰富多样&#xff0c;层出不穷。作为一名UI设计师&#xff0c;有必要了解哪些在线平面图工具既简单又专业。本文将分享6种在线平面图工具&#xff0c;每种在线平面图工…

【程序人生】如何在工作中保持稳定的情绪?

前言 在工作中保持稳定的情绪是现代生活中一个备受关注的话题。随着职场压力和工作挑战的增加&#xff0c;我们常常发现自己情绪波动不定&#xff0c;甚至受到负面情绪的困扰。然而&#xff0c;保持稳定的情绪对于我们的工作效率、人际关系和整体幸福感都至关重要。 无论你是…

JavaEE——SpringMVC中的常用注解

目录 1、RestController &#xff08;1&#xff09;、Controller &#xff08;2&#xff09;、ResponseBody 2、RequestMappping &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、使用 【1】、修饰方法 【2】、修饰类 【3】、指定方法类型 【4】、简化版…

大数据面试题之Elasticsearch:每日三题(六)

大数据面试题之Elasticsearch:每日三题 1. 为什么要使用Elasticsearch&#xff1f;2.Elasticsearch的master选举流程&#xff1f;3.Elasticsearch集群脑裂问题&#xff1f; 1. 为什么要使用Elasticsearch&#xff1f; 系统中的数据&#xff0c;随着业务的发展&#xff0c;时间…

SpringCloudAlibaba微服务实战系列(四)Sentinel熔断降级、异常fallback、block细致处理

SpringCloudAlibaba Sentinel降级和熔断 接着上篇文章的内容&#xff0c;在Sentinel中如何进行降级和熔断呢&#xff1f; 熔断降级规则 降级规则 在Sentinel中降级主要有三个策略&#xff1a;RT、异常比例、异常数&#xff0c;也是针对某个资源的设置。而在1.8.0版本后RT改为…

如何做好项目管理?年薪百万项目大佬一直在用这11张图!

日常工作中&#xff0c;我们会遇到各种大大小小的工作项目&#xff0c;如何能让项目保质保量的完成&#xff0c;就需要项目管理。项目管理是什么&#xff1f;一句话解释&#xff1a;在有限的时间内&#xff0c;在约束的范围中&#xff0c;集合有限资源来完成项目目标。 本周小编…