内容
活动
关注

ClickHouse(02)ClickHouse架构设计介绍概述与ClickHouse数据分片设计

简介: ClickHouse的核心架构包括执行过程和数据存储两部分。执行过程涉及Parser与Interpreter解析SQL,通过Column、DataType、Block、Functions和Storage模块处理数据。Column是内存中列的表示,Field处理单个值,DataType负责序列化和反序列化,Block是内存中表的子集,Block Streams处理数据流。Storage代表表,使用不同的引擎如StorageMergeTree。数据存储基于分片和副本,1个分片由多个副本组成,每个节点只能拥有1个分片。

ClickHouse核心架构设计是怎么样的?ClickHouse核心架构模块分为两个部分:ClickHouse执行过程架构和ClickHouse数据存储架构,下面分别详细介绍。

ClickHouse执行过程架构

总的来说,结合目前搜集到的一些资料,可以看到目前ClickHouse核心架构由下图构成,主要的抽象模块是Column、DataType、Block、Functions、Storage、Parser与Interpreter。

ClickHouse核心架构模块

简单来说,就是一条sql,会经由Parser与Interpreter,解析和执行,通过调用Column、DataType、Block、Functions、Storage等模块,最终返回数据,下面是各个模块具体的介绍。

Columns

表示内存中的列(实际上是列块),需使用 IColumn 接口。该接口提供了用于实现各种关系操作符的辅助方法。几乎所有的操作都是不可变的:这些操作不会更改原始列,但是会创建一个新的修改后的列。

Column对象分为接口和实现两个部分,在IColumn接口对象中,定义了对数据进行各种关系运算的方法,例如插入数据的insertRangeFrom和insertFrom方法、用于分页的cut,以及用于过滤的filter方法等。而这些方法的具体实现对象则根据数据类型的不同,由相应的对象实现,例如ColumnString、ColumnArray和ColumnTuple等。

Field

表示单个值,有时候也可能需要处理单个值,可以使用Field。Field 是 UInt64、Int64、Float64、String 和 Array 组成的联合。与Column对象的泛化设计思路不同,Field对象使用了聚合的设计模式。在Field对象内部聚合了Null、UInt64、String和Array等13种数据类型及相应的处理逻辑。

DataType

IDataType 负责序列化和反序列化:读写二进制或文本形式的列或单个值构成的块。IDataType直接与表的数据类型相对应。比如,有 DataTypeUInt32、DataTypeDateTime、DataTypeString等数据类型。

IDataType与IColumn之间的关联并不大。不同的数据类型在内存中能够用相同的IColumn实现来表示。比如,DataTypeUInt32和DataTypeDateTime都是用ColumnUInt32或ColumnConstUInt32来表示的。另外,相同的数据类型也可以用不同的IColumn实现来表示。比如,DataTypeUInt8既可以使用ColumnUInt8 来表示,也可以使用过ColumnConstUInt8 来表示。

IDataType仅存储元数据。比如,DataTypeUInt8不存储任何东西(除了vptr);DataTypeFixedString仅存储N(固定长度字符串的串长度)。

IDataType具有针对各种数据格式的辅助函数。比如如下一些辅助函数:序列化一个值并加上可能的引号;序列化一个值用于 JSON 格式;序列化一个值作为 XML 格式的一部分。辅助函数与数据格式并没有直接的对应。比如,两种不同的数据格式 Pretty 和 TabSeparated 均可以使用 IDataType 接口提供的 serializeTextEscaped 这一辅助函数。

Block

Block是表示内存中表的子集(chunk)的容器,是由三元组:(IColumn,IDataType,列名)构成的集合。在查询执行期间,数据是按 Block进行处理的。如果我们有一个Block,那么就有了数据(在IColumn对象中),有了数据的类型信息告诉我们如何处理该列,同时也有了列名(来自表的原始列名,或人为指定的用于临时计算结果的名字)。

当我们遍历一个块中的列进行某些函数计算时,会把结果列加入到块中,但不会更改函数参数中的列,因为操作是不可变的。之后,不需要的列可以从块中删除,但不是修改。这对于消除公共子表达式非常方便。

Block用于处理数据块。注意,对于相同类型的计算,列名和类型对不同的块保持相同,仅列数据不同。最好把块数据(block data)和块头(block header)分离开来,因为小块大小会因复制共享指针和列名而带来很高的临时字符串开销。

Block Streams

块流用于处理数据。我们可以使用块流从某个地方读取数据,执行数据转换,或将数据写到某个地方。IBlockInputStream 具有 read 方法,其能够在数据可用时获取下一个块。IBlockOutputStream 具有 write 方法,其能够将块写到某处。

块流负责:

  1. 读或写一个表。表仅返回一个流用于读写块。
  2. 完成数据格式化。比如,如果你打算将数据以Pretty格式输出到终端,你可以创建一个块输出流,将块写入该流中,然后进行格式化。
  3. 执行数据转换。假设你现在有IBlockInputStream并且打算创建一个过滤流,那么你可以创建一个FilterBlockInputStream并用IBlockInputStream 进行初始化。之后,当你从FilterBlockInputStream中拉取块时,会从你的流中提取一个块,对其进行过滤,然后将过滤后的块返回给你。查询执行流水线就是以这种方式表示的。

Storage

IStorage接口表示一张表。该接口的不同实现对应不同的表引擎。比如 StorageMergeTree、StorageMemory等。这些类的实例就是表。

IStorage 中最重要的方法是read和write,除此之外还有alter、rename和drop等方法。read方法接受如下参数:需要从表中读取的列集,需要执行的AST查询,以及所需返回的流的数量。read方法的返回值是一个或多个IBlockInputStream对象,以及在查询执行期间在一个表引擎内完成的关于数据处理阶段的信息。

在大多数情况下,read方法仅负责从表中读取指定的列,而不会进行进一步的数据处理。进一步的数据处理均由查询解释器完成,不由 IStorage 负责。

但是也有值得注意的例外:AST查询被传递给read方法,表引擎可以使用它来判断是否能够使用索引,从而从表中读取更少的数据。有时候,表引擎能够将数据处理到一个特定阶段。比如,StorageDistributed 可以向远程服务器发送查询,要求它们将来自不同的远程服务器能够合并的数据处理到某个阶段,并返回预处理后的数据,然后查询解释器完成后续的数据处理。

Parser与Interpreter

Parser和Interpreter是非常重要的两组接口:Parser分析器负责创建AST对象;而Interpreter解释器则负责解释AST,并进一步创建查询的执行管道。它们与IStorage一起,串联起了整个数据查询的过程。Parser分析器可以将一条SQL语句以递归下降的方法解析成AST语法树的形式。不同的SQL语句,会经由不同的Parser实现类解析。例如,有负责解析DDL查询语句的ParserRenameQuery、ParserDropQuery和ParserAlterQuery解析器,也有负责解析INSERT语句的ParserInsertQuery解析器,还有负责SELECT语句的ParserSelectQuery等。

Interpreter解释器的作用就像Service服务层一样,起到串联整个查询过程的作用,它会根据解释器的类型,聚合它所需要的资源。首先它会解析AST对象;然后执行“业务逻辑”(例如分支判断、设置参数、调用接口等);最终返回IBlock对象,以线程的形式建立起一个查询执行管道。

Functions

函数既有普通函数,也有聚合函数。

普通函数不会改变行数-它们的执行看起来就像是独立地处理每一行数据。实际上,函数不会作用于一个单独的行上,而是作用在以Block 为单位的数据上,以实现向量查询执行。

还有一些杂项函数,比如块大小、rowNumberInBlock,以及跑累积,它们对块进行处理,并且不遵从行的独立性。

ClickHouse 具有强类型,因此隐式类型转换不会发生。如果函数不支持某个特定的类型组合,则会抛出异常。但函数可以通过重载以支持许多不同的类型组合。比如,plus 函数(用于实现+运算符)支持任意数字类型的组合:UInt8+Float32,UInt16+Int8等。同时,一些可变参数的函数能够级接收任意数目的参数,比如concat函数。

实现函数可能有些不方便,因为函数的实现需要包含所有支持该操作的数据类型和IColumn类型。比如,plus函数能够利用C++模板针对不同的数字类型组合、常量以及非常量的左值和右值进行代码生成。

这是一个实现动态代码生成的好地方,从而能够避免模板代码膨胀。同样,运行时代码生成也使得实现融合函数成为可能,比如融合«乘-加»,或者在单层循环迭代中进行多重比较。

由于向量查询执行,函数不会«短路»。比如,如果你写 WHERE f(x) AND g(y),两边都会进行计算,即使是对于 f(x) 为 0 的行(除非f(x)是零常量表达式)。但是如果 f(x) 的选择条件很高,并且计算 f(x) 比计算 g(y) 要划算得多,那么最好进行多遍计算:首先计算 f(x),根据计算结果对列数据进行过滤,然后计算 g(y),之后只需对较小数量的数据进行过滤。

ClickHouse数据存储架构

ClickHouse数据存储架构由分片(Shard)组成,而每个分片又通过副本(Replica)组成。ClickHouse分片有限免两个特点。

  1. ClickHouse的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点。
  2. 分片只是一个逻辑概念,其物理承载还是由副本承担的。

下面是cluster拥有1个shard(分片)和2个replica(副本),且副本由192.37.129.6服务节点和192.37.129.7服务节承载。从本质上看,这个配置是是一个分片一个副本,因为分片最终还是由副本来实现,所以这个其中一个副本是属于分片,分片是一个逻辑概念,它指的是其中的一个副本,这个和Elasticsearch中的分片和副本的概念有所不同。

 <ch_cluster> <shard> <replica> <host>192.37.129.6</host> <port>9000</port> </replica> <replica> <host>192.37.129.7</host> <port>9000</port> </replica> </shard> </ch_cluster> 

资料分享

ClickHouse经典中文文档分享

系列文章

clickhouse系列文章

相关文章
|
25天前
|
存储 SQL 消息中间件
从 ClickHouse 到 StarRocks 存算分离: 携程 UBT 架构升级实践
查询性能实现从秒级到毫秒级的跨越式提升
|
4月前
|
存储 BI Shell
Doris基础-架构、数据模型、数据划分
Apache Doris 是一款高性能、实时分析型数据库,基于MPP架构,支持高并发查询与复杂分析。其前身是百度的Palo项目,现为Apache顶级项目。Doris适用于报表分析、数据仓库构建、日志检索等场景,具备存算一体与存算分离两种架构,灵活适应不同业务需求。它提供主键、明细和聚合三种数据模型,便于高效处理更新、存储与统计汇总操作,广泛应用于大数据分析领域。
478 2
|
3月前
|
数据采集 缓存 前端开发
如何开发门店业绩上报管理系统中的商品数据板块?(附架构图+流程图+代码参考)
本文深入讲解门店业绩上报系统中商品数据板块的设计与实现,涵盖商品类别、信息、档案等内容,详细阐述技术架构、业务流程、数据库设计及开发技巧,并提供完整代码示例,助力企业构建稳定、可扩展的商品数据系统。
|
4月前
|
SQL 缓存 前端开发
如何开发进销存系统中的基础数据板块?(附架构图+流程图+代码参考)
进销存系统是企业管理采购、销售与库存的核心工具,能有效提升运营效率。其中,“基础数据板块”作为系统基石,决定了后续业务的准确性与扩展性。本文详解产品与仓库模块的设计实现,涵盖功能概述、表结构设计、前后端代码示例及数据流架构,助力企业构建高效稳定的数字化管理体系。
|
2月前
|
数据采集 机器学习/深度学习 搜索推荐
MIT新论文:数据即上限,扩散模型的关键能力来自图像统计规律,而非复杂架构
MIT与丰田研究院研究发现,扩散模型的“局部性”并非源于网络架构的精巧设计,而是自然图像统计规律的产物。通过线性模型仅学习像素相关性,即可复现U-Net般的局部敏感模式,揭示数据本身蕴含生成“魔法”。
139 3
MIT新论文:数据即上限,扩散模型的关键能力来自图像统计规律,而非复杂架构
|
1月前
|
Java Linux 虚拟化
【Docker】(1)Docker的概述与架构,手把手带你安装Docker,云原生路上不可缺少的一门技术!
1. Docker简介 1.1 Docker是什么 为什么docker会出现? 假定您在开发一款平台项目,您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。 您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。 您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列支持文件。 **要求:**希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销 问题: 要如何确保应用能够在这些环境中运行和通过质量检测? 在部署过程中不出现令人头疼的版本、配置问题 无需重新编写代码和进行故障修复
280 2
|
2月前
|
JSON 供应链 监控
1688商品详情API技术深度解析:从接口架构到数据融合实战
1688商品详情API(item_get接口)可通过商品ID获取标题、价格、库存、SKU等核心数据,适用于价格监控、供应链管理等场景。支持JSON格式返回,需企业认证。Python示例展示如何调用接口获取商品信息。
|
3月前
|
数据采集 监控 数据可视化
数据量暴涨时,抓取架构该如何应对?——豆瓣电影案例调研
本案例讲述了在豆瓣电影数据采集过程中,面对数据量激增和限制机制带来的挑战,如何通过引入爬虫代理、分布式架构与异步IO等技术手段,实现采集系统的优化与扩展,最终支撑起百万级请求的稳定抓取。
129 0
数据量暴涨时,抓取架构该如何应对?——豆瓣电影案例调研
|
3月前
|
SQL 数据采集 数据处理
终于有人把数据架构讲清楚了!
本文深入浅出地解析了数据架构的核心逻辑,涵盖其定义、作用、设计方法及常见误区,助力读者构建贴合业务的数据架构。
|
4月前
|
数据采集 存储 分布式计算
一文读懂数据中台架构,高效构建企业数据价值
在数字化时代,企业面临数据分散、难以统一管理的问题。数据中台架构通过整合、清洗和管理数据,打破信息孤岛,提升决策效率。本文详解其核心组成、搭建步骤及常见挑战,助力企业高效用数。
1518 24

热门文章

最新文章

推荐镜像

查看更多
下一篇