第1章 ClickHouse的前世今生
- 在大量数据分析场景的解决方案中,传统关系型数据库很快就被
Hadoop
生态所取代 - 传统关系型数据库所构建的数据仓库,被以
Hive
为代表的大数据技术所取代 - 数据查询分析的手段也层出不穷,
Spark
、Impala
、Kylin
等百花齐放
1.1 传统BI系统之殇
- 企业在生产经营的过程中,并不是只关注诸如流程审批、数据录入和填报这类工作。站在监管和决策层面,还需要另一种分析类视角,例如分析报表、分析决策等。而
IT
系统在早期的建设过程中多呈烟囱式发展,数据散落在各个独立的系统之内,相互割裂、互不相通 - 为了
解决数据孤岛
的问题,人们提出了数据仓库的概念。即通过引入一个专门用于分析类场景的数据库,将分散的数据统一汇聚到一处。借助数据仓库的概念,用户第一次拥有了站在企业全局鸟瞰一切数据的视角 - 相对于联机事务处理系统,我们把这类
BI
系统称为联机分析(OLAP
)系统
1.2 现代BI系统的新思潮
- 根据一项调查,互联网用户通常都没有耐心。例如47%的消费者希望在2秒或更短的时间内完成网页加载
SaaS
模式的兴起,为传统企业软件系统的商业模式带来了新的思路,这是一次新的技术普惠
- 一方面,
SaaS
模式将之前只服务于中大型企业的软件系统放到了互联网,扩展了它的受众; - 另一方面,由于互联网用户的基本特征和软件诉求,又倒逼了这些软件系统在方方面面进行革新与升级
2003年起,
Hadoop
生态由此开始一发不可收拾,数据分析开启了新纪元。从某种角度来看,以使用Hadoop
生态为代表的这类非传统关系型数据库技术所实现的BI
系统,可以称为现代BI
系统
- 在海量数据下要实现多维分析的实时应答,仍旧困难重重。(现代
BI
系统的典型应用场景是多维分析,某些时候可以直接使用OLAP
指代这类场景。)
1.3 OLAP常见架构分类
OLAP
名为联机分析,又可以称为多维分析,是由关系型数据库之父埃德加·科德(EdgarFrankCodd
)于1993年提出的概念。顾名思义,它指的是通过多种不同的维度审视数据,进行深层次分析- 数据立方体可以进行如下操作
- 下钻:从高层次向低层次明细数据穿透。例如从“省”下钻到“市
- 上卷:和下钻相反,从低层次向高层次汇聚。例如从“市”汇聚成“省
- 切片:观察立方体的一层,将一个或多个维度设为单个固定值,然后观察剩余的维度,例如将商品维度固定为“足球
- 切块:与切片类似,只是将单个固定值变成多个值。例如将商品维度固定成“足球”“篮球
- 旋转:旋转立方体的一面,如果要将数据映射到一张二维表,那么就要进行旋转,这就等同于行列置换
OLAP架构大致分成三类
ROLAP(RelationalOLAP,关系型OLAP)
- 顾名思义,它直接使用关系模型构建,数据模型常使用星型模型或者雪花模型
MOLAP(MultidimensionalOLAP,多维型OLAP)
- 它的出现是为了缓解
ROLAP
性能问题。MOLAP
使用多维数组的形式保存数据,其核心思想是借助预先聚合结果,使用空间换取时间的形式最终提升查询性能
- 维度预处理可能会导致数据的膨胀
- 其立方体预聚合后的数据量可能会达到10到20倍的膨胀
- 由于使用了预处理的形式,数据立方体会有一定的滞后性,不能实时进行数据分析
HOLAP(HybridOLAP,混合架构的OLAP)
- 这种思路可以理解成
ROLAP
和MOLAP
两者的集成
1.4 OLAP实现技术的演进
- 演进过程简单划分成两个阶段
传统关系型数据库阶段
- 在这个阶段中,
OLAP
主要基于以Oracle
、MySQL
为代表的一众关系型数据实现。在这个时期,不论是ROLAP
还是MOLAP
,在数据体量大、维度数目多的情况下都存在严重的性能问题
大数据技术阶段
- 由于大数据处理技术的普及,人们开始使用大数据技术重构
ROLAP
和MOLAP
。以ROLAP
架构为例,传统关系型数据库就被Hive
和SparkSQL
这类新兴技术所取代 - 大数据技术阶段,主流
MOLAP
架构已经能够在亿万级数据的体量下,实现毫秒级的查询响应时间。尽管如此,MOLAP
架构依然存在维度爆炸、数据同步实时性不高的问题
1.5 一匹横空出世的黑马
- 一款具备现代化
SaaS
属性的BI
分析类产品 - 必须至少具备如下特征
- 一站式:下至数百条数据的个人
Excel
表格,上至数亿级别的企业数据,都能够在系统内部被直接处理 - 自服务,简单易用:面向普通用户而非专业
IT
人员,通过简单拖拽或搜索维度,就能完成初步的分析查询 - 实时应答:无论数据是什么体量级别,查询必须在毫秒至1秒内返回
- 专业化、智能化:需要具备专业化程度并具备智能化的提升空间,需要提供专业的数学方法
- 具有
ROLAP
、在线实时查询、完整的DBMS
、列式存储、不需要任何数据预处理、支持批量更新、拥有非常完善的SQL
支持和函数、支持高可用、不依赖Hadoop
复杂生态、开箱即用等许多特点 - 对一张拥有133个字段的数据表分别在1000万、1亿和10亿三种数据体量下执行基准测试,基准测试的范围涵盖43项
SQL
查询。在1亿数据集体量的情况下,ClickHouse
的平均响应速度是Vertica
的2.63倍、InfiniDB
的17倍、MonetDB
的27倍、Hive
的126倍、MySQL
的429倍以及Greenplum
的10倍
详细测试结果:
https://clickhouse.yandex/benchmark.html
1.6 ClickHouse的发展历程
ClickHouse
背后的研发团队是来自俄罗斯的Yandex
公司。这是一家俄罗斯本土的互联网企业,于2011年在纳斯达克上市,它的核心产品是搜索引擎。根据最新的数据显示,Yandex
占据了本国47%以上的搜索市场,是现今世界上最大的俄语搜索引擎ClickHouse
就是在这样的产品背景下诞生的,伴随着Yandex.Metrica
业务的发展,其底层架构历经四个阶段Yandex.Metrica
研发团队以OLAPServer
为基础进一步完善,以实现一个完备的数据库管理系统(DBMS
)为目标,最终打造出了ClickHouse
,并于2016年开源
ClickHouse
的发展历程
1.7 ClickHouse的名称含义
- 而在采集数据的过程中,一次页面
click
(点击),会产生一个event
(事件)。至此,整个系统的逻辑就十分清晰了,那就是基于页面的点击事件流,面向数据仓库进行OLAP
分析。所以ClickHouse
的全称是ClickStream
,DataWareHouse
,简称ClickHouse
1.8 ClickHouse适用的场景
- 在存储数据超过20万亿行的情况下,
ClickHouse
做到了90%的查询都能够在1秒内返回的惊人之举 ClickHouse
非常适用于商业智能领域(也就是我们所说的BI
领域),除此之外,它也能够被广泛应用于广告流量、Web
、App
流量、电信、金融、电子商务、信息安全、网络游戏、物联网等众多其他领域
1.9 ClickHouse不适用的场景
- 它有以下几点不足
- 不支持事务
- 不擅长根据主键按行粒度进行查询(虽然支持),故不应该把
ClickHouse
当作KeyValue
数据库使用 - 不擅长按行删除数据(虽然支持)
1.10 有谁在使用ClickHouse
ClickHouse
官网的案例介绍(https://clickhouse.yandex
/)
第2章 ClickHouse架构概述
Yandex.Metrica
目前已经成为世界第三大Web
流量分析平台,每天处理超过200
亿个跟踪事件。能够拥有如此惊人的体量,在它背后提供支撑的ClickHouse
功不可没。ClickHouse
已经为Yandex.Metrica
存储了超过20万亿行的数据,90%的自定义查询能够在1秒内返回,其集群规模也超过了400台服务器
ClickHouse
更像一款“传统”MPP
架构的数据库,它没有采用Hadoop
生态中常用的主从架构,而是使用了多主对等网络结构,同时它也是基于关系模型的ROLAP
方案
2.1 ClickHouse的核心特性
ClickHouse
是一款MPP
架构的列式存储数据库
完备的DBMS功能
DDL
(数据定义语言)DML
(数据操作语言)- 权限控制
- 数据备份与恢复
- 分布式管理
列大存储与数据压缩
- 如果你想让查询变得更快,最简单且有效的方法是减少数据扫描范围和数据传输时的大小,而列式存储和数据压缩就可以帮助我们实现上述两点
- 数据中的重复项越多,则压缩率越高;压缩率越高
- 数据最可能具备重复的特性呢?答案是属于同一个列字段的数据,因为它们拥有相同的数据类型和现实语义,重复项的可能性自然就更高
向量化执行引擎
- 这项寄存器硬件层面的特性,为上层应用程序的性能带来了指数级的提升
- 向量化执行,可以简单地看作一项消除程序中循环的优化
- 为了制作
n
杯果汁,非向量化执行的方式是用1
台榨汁机重复循环制作n
次,而向量化执行的方式是用n
台榨汁机只执行1
次 - 为了实现向量化执行,需要利用
CPU
的SIMD
指令。SIMD
的全称是SingleInstructionMultipleData
,即用单条指令操作多条数据。现代计算机系统概念中,它是通过数据并行以提高性能的一种实现方式(其他的还有指令级并行和线程级并行),它的原理是在CPU
寄存器层面实现数据的并行操作 - 一个实用的经验告诉我们,存储媒介距离
CPU
越近,则访问数据的速度越快
图21 距离
CPU
越远,数据的访问速度越慢
- 所以利用
CPU
向量化执行的特性,对于程序的性能提升意义非凡 ClickHouse
目前利用SSE
4.2指令集实现向量化执行
关系模型与SQL查询
ClickHouse
完全使用SQL
作为查询语言(支持GROUPBY
、ORDERBY
、JOIN
、IN
等大部分标准SQL
),这使得它平易近人,容易理解和学习- 在
SQL
解析方面,ClickHouse
是大小写敏感的,这意味着SELECTa
和SELECTA
所代表的语义是不同的 ClickHouse
使用了关系模型,所以将构建在传统关系型数据库或数据仓库之上的系统迁移到ClickHouse
的成本会变得更低
多样化的表引擎
ClickHouse
共拥有合并树、内存、文件、接口和其他6大类20多种表引擎。其中每一种表引擎都有着各自的特点,用户可以根据实际业务场景的要求,选择合适的表引擎使用
多线程与分布式
- 由于
SIMD
不适合用于带有较多分支判断的场景,ClickHouse
也大量使用了多线程技术以实现提速,以此和向量化执行形成互补 ClickHouse
在数据存取方面,既支持分区(纵向扩展,利用多线程原理),也支持分片(横向扩展,利用分布式原理),可以说是将多线程和分布式的技术应用到了极致
多主架构
HDFS
、Spark
、HBase
和Elasticsearch
这类分布式系统,都采用了MasterSlave
主从架构,由一个管控节点作为Leader
统筹全局。而ClickHouse
则采用MultiMaster
多主架构,集群中的每个节点角色对等,客户端访问任意一个节点都能得到相同的效果。这种多主的架构有许多优势,例如对等的角色使系统架构变得更加简单,不用再区分主控节点、数据节点和计算节点,集群中的所有节点功能相同。所以它天然规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景
在线查询
ClickHouse
经常会被拿来与其他的分析型数据库作对比,比如Vertica
、SparkSQL
、Hive
和Elasticsearch
等,它与这些数据库确实存在许多相似之处。例如,它们都可以支撑海量数据的查询场景,都拥有分布式架构,都支持列存、数据分片、计算下推等特性。这其实也侧面说明了ClickHouse
在设计上确实吸取了各路奇技淫巧。与其他数据库相比,ClickHouse
也拥有明显的优势。
Vertica
这类商用软件价格高昂SparkSQL
与Hive
这类系统无法保障90%的查询在1秒内返回,在大数据量下的复杂查询可能会需要分钟级的响应时间;Elasticsearch
这类搜索引擎在处理亿级数据聚合查询时则显得捉襟见肘
ClickHouse
的“广告词”所言,其他的开源系统太慢,商用的系统太贵,只有Clickouse
在成本与性能之间做到了良好平衡,即又快又开源
数据分片与分布式查询
- 数据分片是将数据进行横向切分,这是一种在面对海量数据的场景下,解决存储和查询瓶颈的有效手段,是一种分治思想的体现。
ClickHouse
支持分片,而分片则依赖集群。每个集群由1到多个分片组成,而每个分片则对应了ClickHouse
的1个服务节点。分片的数量上限取决于节点数量(1个分片只能对应1个服务节点) ClickHouse
提供了本地表(LocalTable
)与分布式表(DistributedTable
)的概念。一张本地表等同于一份数据的分片。而分布式表本身不存储任何数据,它是本地表的访问代理,其作用类似分库中间件。借助分布式表,能够代理访问多个数据分片,从而实现分布式查询- 这种设计类似数据库的分库和分表,十分灵活。例如在业务系统上线的初期,数据体量并不高,此时数据表并不需要多个分片。所以使用单个节点的本地表(单个数据分片)即可满足业务需求,待到业务增长、数据量增大的时候,再通过新增数据分片的方式分流数据,并通过分布式表实现分布式查询。这就好比一辆手动挡赛车,它将所有的选择权都交到了使用者的手中
2.2 ClickHouse的架构设计
Column与Field
Column
和Field
是ClickHouse
数据最基础的映射单元ClickHouse
按列存储数据,内存中的一列数据由一个Column
对象表示- 如果需要操作单个具体的数值(也就是单列中的一行数据),则需要使用
Field
对象,Field
对象代表一个单值 Field
对象内部聚合了Null
、UInt
64、String
和Array
等13种数据类型及相应的处理逻辑
图22
ClickHouse
架构设计中的核心模块
DataType
- 数据的序列化和反序列化工作由
DataType
负责。IDataType
接口定义了许多正反序列化的方法,它们成对出现,例如serializeBinary
和deserializeBinary
- 在
DataType
的实现类中,聚合了相应数据类型的Column
对象和Field
对象
Block与Block流
Block
对象的本质是由数据对象、数据类型和列名称组成的三元组,即Column
、DataType
及列名称字符串- 对
Block
流的设计就是水到渠成的事情了。流操作有两组顶层接口:IBlockInputStream
负责数据的读取和关系运算,IBlockOutputStream
负责将数据输出到下一环节
Table
- 在数据表的底层设计中并没有所谓的
Table
对象,它直接使用IStorage
接口指代数据表
Functions与Aggregate Functions
ClickHouse
主要提供两类函数——普通函数和聚合函数。普通函数由IFunction
接口定义,拥有数十种函数实现- 聚合函数由
IAggregateFunction
接口定义,相比无状态的普通函数,聚合函数是有状态的
Cluster与Replication
ClickHouse
的集群由分片(Shard
)组成,而每个分片又通过副本(Replica
)组成。这种分层的概念,在一些流行的分布式系统中十分普遍。例如,在Elasticsearch
的概念中,一个索引由分片和副本组成,副本可以看作一种特殊的分片。如果一个索引由5个分片组成,副本的基数是1,那么这个索引一共会拥有10个分片(每1个分片对应1个副本)ClickHouse
的某些设计总是显得独树一帜
ClickHouse
的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点- 分片只是一个逻辑概念,其物理承载还是由副本承担的
代码清单21 自定义集群
ch_cluster
的配置示例
2.3 ClickHouse为何如此之快
- 在设计软件架构的时候,做设计的原则应该是自顶向下地去设计,还是应该自下而上地去设计呢?在传统观念中,或者说在我的观念中,自然是自顶向下的设计,通常我们都被教导要做好顶层设计。而
ClickHouse
的设计则采用了自下而上的方式。ClickHouse
的原型系统早在2008年就诞生了,在诞生之初它并没有宏伟的规划。相反它的目的很单纯,就是希望能以最快的速度进行GROUPBY
查询和过滤
着眼硬件,先想后做
- 从硬件功能层面着手设计,在设计伊始就至少需要想清楚如下几个问题
- 我们将要使用的硬件水平是怎样的?包括
CPU
、内存、硬盘、网络等 - 在这样的硬件上,我们需要达到怎样的性能?包括延迟、吞吐量等
- 我们准备使用怎样的数据结构?
- 基于将硬件功效最大化的目的,
ClickHouse
会在内存中进行GROUPBY
,并且使用HashTable
装载数据。与此同时,他们非常在意CPUL
3级别的缓存,因为一次L3
的缓存失效会带来70~100ns
的延迟。这意味着在单核CPU
上,它会浪费4000万次/秒
的运算;而在一个32线程的CPU
上,则可能会浪费5亿次/秒
的运算。所以别小看这些细节,一点一滴地将它们累加起来,数据是非常可观的。正因为注意了这些细节,所以ClickHouse
在基准查询中能做到1.75亿次/秒
的数据扫描性能
算法在前,抽象在后
- 在字符串搜索方面,针对不同的场景,
ClickHouse
最终选择了这些算法:对于常量,使用Volnitsky
算法;对于非常量,使用CPU
的向量化执行SIMD
,暴力优化;正则匹配使用re2
和hyperscan
算法。性能是算法选择的首要考量指标
勇于尝鲜,不行就换
ClickHouse
会使用最合适、最快的算法。如果世面上出现了号称性能强大的新算法,ClickHouse
团队会立即将其纳入并进行验证
特定场景,特殊优化
- 大家熟知的大杀器——向量化执行了。
SIMD
被广泛地应用于文本转换、数据过滤、数据解压和JSON
转换等场景。相较于单纯地使用CPU
,利用寄存器暴力优化也算是一种降维打击了
第3章 安装与部署
ClickHouse
的安装显得尤为简单,它自成一体,在单节点的情况下不需要额外的系统依赖
3.2 客户端的访问接口
ClickHouse
的底层访问接口支持TCP
和HTTP
两种协议,其中,TCP
协议拥有更好的性能,其默认端口为9000- 通常而言,并不建议用户直接使用底层接口访问
ClickHouse
,更为推荐的方式是通过CLI
和JDBC
这些封装接口,因为它们更加简单易用
CLI
CLI
(CommandLineInterface
)即命令行接口,其底层是基于TCP
接口进行通信的,是通过clickhouseclient
脚本运行的。它拥有两种执行模式- 交互式执行可以广泛用于调试、运维、开发和测试等场景,它的使用方法是直接运行
clickhouseclient
进行登录
- 通过交互式执行的
SQL
语句,相关查询结果会统一被记录到~/.clickhouseclienthistory
文件
- 非交互式模式主要用于批处理场景,诸如对数据的导入和导出等操作。在执行脚本命令时,需要追加
query
参数指定执行的SQL
语句
- 可以追加
multiquery
参数,它可以支持一次运行多条SQL
查询,多条查询语句之间使用分号间隔
3.3 内置的实用工具
clickhouse-local
clickhouselocal
可以独立运行大部分SQL
查询,不需要依赖任何ClickHouse
的服务端程序,它可以理解成是ClickHouse
服务的单机版微内核,是一个轻量级的应用程序。clickhouselocal
只能够使用File
表引擎
- 也可以借助操作系统的命令,实现对系统用户内存用量的查询
clickhousebenchmark
是基准测试的小工具,它可以自动运行SQL
查询,并生成相应的运行指标报告,例如执行下面的语句启动测试
- 可以指定多条
SQL
进行测试,此时需要将SQL
语句定义在文件中
clickhousebenchmark
支持对比测试,此时需要通过此参数声明两个服务端的地址
第4章 数据定义
- 对于一款可以处理海量数据的分析系统而言,支持
DML
查询实属难能可贵 ClickHouse
支持较完备的DML
语句,包括INSERT
、SELECT
、UPDATE
和DELETE
。虽然UPDATE
和DELETE
可能存在性能问题,但这些能力的提供确实丰富了各位架构师手中的筹码,在架构设计时也能多几个选择- 作为一款完备的
DBMS
(数据库管理系统),ClickHouse
提供了DDL
与DML
的功能,并支持大部分标准的SQL
。也正因如此,ClickHouse
十分容易入门 - 作为一款异军突起的
OLAP
数据库黑马,ClickHouse
有着属于自己的设计目标,高性能才是它的根本,所以也不能完全以对传统数据库的理解度之。比如,ClickHouse
在基础数据类型方面,虽然相比常规数据库更为精练,但同时它又提供了实用的复合数据类型,而这些是常规数据库所不具备的
4.1 ClickHouse的数据类型
ClickHouse
提供了许多数据类型,它们可以划分为基础类型、复合类型和特殊类型- 基础类型
- 数值
- 字符串
- 时间
- 数值类型
- 整数
- 浮点数
- 定点数
Int
类型
Int
8Int
16Int
32Int
64
其末尾的数字正好表明了占用字节的大小(8位=1字节)
ClickHouse
的浮点数
- 正无穷
- 负无穷
- 非数字
- 在使用定点数时还有一点值得注意:由于现代计算器系统只支持32位和64位
CPU
,所以Decimal
128是在软件层面模拟实现的,它的速度会明显慢于Decimal
32与Decimal
64 - 字符串类型
String
FixedString
UUID
- 时间类型分为
DateTime
、DateTime
64和Date
三类。ClickHouse
目前没有时间戳类型。时间类型最高的精度是秒,也就是说,如果需要处理毫秒、微秒等大于秒分辨率的时间,则只能借助UInt
类型实现 DateTime
64可以记录亚秒,它在DateTime
之上增加了精度的设置- 四类复合类型
- 数组:在同一个数组内可以包含多种数据类型,例如数组[1,2.0]是可行的。但各类型之间必须兼容,例如数组[1,'2']则会报错
- 元组:元组类型由1~
n
个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容 - 枚举
- 嵌套:嵌套类型,顾名思义是一种嵌套表结构。一张数据表,可以定义任意多个嵌套类型字段,但每个字段的嵌套层级只支持一级。每个数组的元素个数必须相等。在访问嵌套类型的数据时需要使用点符号
- 准确来说,
Nullable
并不能算是一种独立的数据类型,它更像是一种辅助的修饰符,需要与基础数据类型一起搭配使用 - 在使用
Nullable
类型的时候还有两点值得注意
- 首先,它只能和基础类型搭配使用,不能用于数组和元组这些复合类型,也不能作为索引字段;
- 应该慎用
Nullable
类型,包括Nullable
的数据表,不然会使查询和写入性能变慢。因为在正常情况下,每个列字段的数据会被存储在对应的[Column].bin
文件中。如果一个列字段被Nullable
类型修饰后,会额外生成一个[Column].null.bin
文件专门保存它的Null
值。这意味着在读取和写入数据时,需要一倍的额外文件操作
- 域名类型
IPv4
IPv6
- 本质上它们是对整型和字符串的进一步封装。
IPv
4类型是基于UInt
32封装的
4.2 如何定义数据表
- 数据库目前一共支持5种引擎
Ordinary
:默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使用任意类型的表引擎Dictionary
:字典引擎Memory
:内存引擎,用于存放临时数据Lazy
:日志引擎,此类数据库下只能使用Log
系列的表引擎MySQL
:MySQL
引擎,此类数据库下会自动拉取远端MySQL
中的数据,并为它们创建MySQL
表引擎的数据表
- 默认数据库的实质是物理磁盘上的一个文件目录,所以在语句执行之后,
ClickHouse
便会在安装路径下创建DB_TEST
数据库的文件目录 - 在
metadata
路径下也会一同创建用于恢复数据库的DB_TEST.sql
文件 ClickHouse
目前提供了三种最基本的建表方法
- 使用[
db_name
.]参数可以为数据表指定数据库,如果不指定此参数,则默认会使用default
数据库 - 第二种定义方法是复制其他表的结构
- 第三种定义方法是通过
SELECT
子句的形式创建
- 在这种方式下,不仅会根据
SELECT
子句建立相应的表结构,同时还会将SELECT
子句查询的数据顺带写入 - 表字段支持三种默认值表达式的定义方法,分别是
DEFAULT
、MATERIALIZED
和ALIAS
- 表字段一旦被定义了默认值,它便不再强制要求定义数据类型,因为
ClickHouse
会根据默认值进行类型推断 - 默认值表达式的三种定义方法之间也存在着不同之处
- 数据写入:在数据写入时,只有
DEFAULT
类型的字段可以出现在INSERT
语句中。而MATERIALIZED
和ALIAS
都不能被显式赋值,它们只能依靠计算取值 - 数据查询:在数据查询时,只有
DEFAULT
类型的字段可以通过SELECT
返回。而MATERIALIZED
和ALIAS
类型的字段不会出现在SELECT
查询的返回结果集中 - 数据存储:在数据存储时,只有
DEFAULT
和MATERIALIZED
类型的字段才支持持久化。如果使用的表引擎支持物理存储(例如TinyLog
表引擎),那么这些列字段将会拥有物理存储。而ALIAS
类型的字段不支持持久化,它的取值总是需要依靠计算产生,数据不会落到磁盘
- 修改动作并不会影响数据表内先前已经存在的数据。但是默认值的修改有诸多限制,例如在合并树表引擎中,它的主键字段是无法被修改
ClickHouse
也有临时表的概念,创建临时表的方法是在普通表的基础之上添加TEMPORARY
关键字- 临时表特殊之处
- 它的生命周期是会话绑定的,所以它只支持
Memory
表引擎,如果会话结束,数据表就会被销毁; - 临时表不属于任何数据库,所以在它的建表语句中,既没有数据库参数也没有表引擎参数。
临时表的优先级是大于普通表的。当两张数据表名称相同的时候,会优先读取临时表的数据
- 数据分区(
partition
)和数据分片(shard
)是完全不同的两个概念。 - 数据分区是针对本地数据而言的,是数据的一种纵向切分。而数据分片是数据的一种横向切分(第10章会详细介绍)。数据分区对于一款
OLAP
数据库而言意义非凡:借助数据分区,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作 - 目前只有合并树(
MergeTree
)家族系列的表引擎才支持数据分区 - 通过一个简单的例子演示分区表的使用方法。首先由
PARTITIONBY
指定分区键,例如下面的数据表partition_v
1使用了日期字段作为分区键,并将其格式化为年月的形式:
- 接着写入不同月份的测试数据:
- 最后通过
system.parts
系统表,查询数据表的分区状态:
partition_v
1按年月划分后,目前拥有两个数据分区,且每个分区都对应一个独立的文件目录,用于保存各自部分的数据
两种视图
普通视图
- 只是一层简单的查询代理
- 普通视图不会存储任何数据,它只是一层单纯的
SELECT
查询映射,起着简化查询、明晰语义的作用,对查询性能不会有任何增强
物化视图
- 物化视图拥有独立的存储
- 物化视图支持表引擎,数据保存形式由它的表引擎决定
- 物化视图创建好之后,如果源表被写入新数据,那么物化视图也会同步更新。
POPULATE
修饰符决定了物化视图的初始化策略:如果使用了POPULATE
修饰符,那么在创建视图的过程中,会连带将源表中已存在的数据一并导入,如同执行了SELECTINTO
一般;反之,如果不使用POPULATE
修饰符,那么物化视图在创建之后是没有数据的,它只会同步在此之后被写入源表的数据。物化视图目前并不支持同步删除,如果在源表中删除了数据,物化视图的数据仍会保留 - 物化视图本质是一张特殊的数据表,例如使用
SHOWTABLE
查看数据表的列表: - 删除视图的方法是直接使用
DROPTABLE
查询
4.3 数据表的基本操作
- 目前只有
MergeTree
、Merge
和Distributed
这三类表引擎支持ALTER
查询
4.4 数据分区的基本操作
ClickHouse
内置了许多system
系统表,用于查询自身的状态信息。其中parts
系统表专门用于查询数据表的分区信息- 如果数据表某一列的数据有误,需要将其重置为初始值,此时可以使用下面的语句实现:
- 表分区可以通过
DETACH
语句卸载,分区被卸载后,它的物理数据并没有删除,而是被转移到了当前数据表目录的detached
子目录下。而装载分区则是反向操作,它能够将detached
子目录下的某个分区重新装载回去。卸载与装载这一对伴生的操作,常用于分区数据的迁移和备份场景 - 一旦分区被移动到了
detached
子目录,就代表它已经脱离了ClickHouse
的管理,ClickHouse
并不会主动清理这些文件。这些分区文件会一直存在,除非我们主动删除或者使用ATTACH
语句重新装载它们
4.5 分布式DDL执行
ClickHouse
支持集群模式,一个集群拥有1到多个节点。CREATE
、ALTER
、DROP
、RENMAE
及TRUNCATE
这些DDL
语句,都支持分布式执行- 将一条普通的
DDL
语句转换成分布式执行十分简单,只需加上ONCLUSTERcluster_name
声明即可
4.6 数据的写入
INSERT
语句支持三种语法范式,三种范式各有不同,可以根据写入的需求灵活运用
- 第一种是使用
VALUES
格式的常规语法:
- 第二种是使用指定格式的语法:
- 第三种是使用
SELECT
子句形式的语法:
- 在通过
SELECT
子句写入数据的时候,同样也支持加入表达式或函数,例如:
VALUES
和SELECT
子句的形式都支持声明表达式或函数,但是表达式和函数会带来额外的性能开销,从而导致写入性能的下降。所以如果追求极致的写入性能,就应该尽可能避免使用它们ClickHouse
内部所有的数据操作都是面向Block
数据块的,所以INSERT
查询最终会将数据转换为Block
数据块。也正因如此,INSERT
语句在单个数据块的写入过程中是具有原子性的max_insert_block_size
参数在使用CLI
命令行或者INSERTSELECT
子句写入时是不生效的
4.7 数据的删除与修改
ClickHouse
提供了DELETE
和UPDATE
的能力,这类操作被称为Mutation
查询,它可以看作ALTER
语句的变种。虽然Mutation
能最终实现修改和删除,但不能完全以通常意义上的UPDATE
和DELETE
来理解,我们必须清醒地认识到它的不同:
- 首先,
Mutation
语句是一种“很重”的操作,更适用于批量数据的修改和删除; - 其次,它不支持事务,一旦语句被提交执行,就会立刻对现有数据产生影响,无法回滚;
- 最后,
Mutation
语句的执行是一个异步的后台过程,语句被提交之后就会立即返回。
所以这并不代表具体逻辑已经执行完毕,它的具体执行进度需要通过
system.mutations
系统表查询
第5章 数据字典
- 字典中的数据会主动或者被动(数据是在
ClickHouse
启动时主动加载还是在首次查询时惰性加载由参数设置决定)加载到内存,并支持动态更新。由于字典数据常驻内存的特性,所以它非常适合保存常量或经常使用的维度表数据,以避免不必要的JOIN
查询。