《ClickHouse原理解析与应用实践》
由于业务上要用到ClickHouse,因此花了两三天看了此书,讲的不是特别深,但是挺适合作为ClickHouse使用者的入门,常用的使用姿势和应用需要了解的一些原理都有讲到。
ClickHouse在线文档:(https://clickhouse.com/docs/zh/)[https://clickhouse.com/docs/zh/]
第一部分,背景篇
OLTP→OLAP
ROLAP,直接使用关系模型构建。海量数据问题。
MOLAP,使用多维数组的形式保存数据,其核心思想是借助预先聚合结果,使用空间换取时间的形式最终提升查询性能。数据膨胀和滞后性问题。
HOLAP,混合上面两种
数据立方体概念
MyISAM引擎使用B+树结构存储索引,而数据则使用另外单独的存储文件;InnoDB引擎使用B+树同时存储索引和数据,数据直接挂载在叶子节点中。
HBase使用LSM树索引结构,发源于Google的BigTable。LSM本质上可以看做是将一颗大树拆成了许多小树。先在内存中构建一颗小树,小树构建过程中会进行排序,小树的数量达到某个阈值输入磁盘生成一小段数据。在每个数据段中,数据局部有序。
发展历程:mysql→Metrage→OLAPServer→ClickHouse
不适用场景:
1、不支持事务
2、不擅长根据主键按行粒度进行查询(虽然支持)
3、不擅长按行删除数据(虽然支持)
ClickHouse拥有完备的管理功能,是一个DBMS(数据库管理系统),而不仅仅是一个数据库。
列式存储和数据压缩通常是伴生的,ClickHouse默认使用L4Z算法压缩,在Yandex的生成环境,数据总体压缩比可以达到8:1。
ClickHouse目前利用SSE4.2指令集实现向量化执行
ClickHouse架构设计中的核心模块:(图)
Data Streams,Server,Parsers、Interpreters,Storages,Columns,DataTypes,Functions
ClickHouse的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点。
分片只是一个逻辑概念,其物理承载还是由副本承担的。
SIMD被广泛应用于文本转换、数据过滤、数据解压和JSON转换等场景。
第二部分,基础篇
基础类型:数值、字符串、时间(没有Boolean)
数值类型分为:整数(Int)、浮点数(Float)、定点数(Decimal)
字符串类型分为:String、FixedString、UUID
时间类型:DateTime、DateTime64、Date,最高精度是秒
复合类型:数组(Array)、元组(Tuple)、枚举(Enum)、嵌套(Nested)
特殊类型:
Nullable,只能和基础类型搭配使用,不能作为索引字段
Domain,IPv6、IPv6
数据库引擎类型:
Ordinary:默认引擎
Dictionary:字典引擎,自动为所有数据字典创建他们的数据表
Memory:内存引擎,临时数据,只停留在内存中
Lazy:日志引擎,只能使用Log系列的表引擎
MySQL:MySQL引擎,会自动拉取远端MySQL中的数据,并为它们创建MySQL表引擎的数据表
三种基本的建表方法:常规定义、复制其他表结构、SELECT字句形式
表字段支持三种默认值表达式的定义方法:DEFAULT、MATERIALIZED、ALIAS
临时表 create temporary table …,只支持Memory表引擎,优先级大于普通表
分区表,数据分区(partition)和数据分片(shard)是完全不同的两个概念。数据分区是针对本地数据而言的,是数据的一种纵向切分。而数据分片是数据的一种横向切分。
只有合并树(MergeTree)家族系列的表引擎才支持数据分区,通过partition by指定分区键
普通视图:create view view_name as select …
不存储任何数据,只是一层单纯的SELECT查询映射,简化查询、明晰语义的作用,对查询性能不会有任何增强。
物化视图:create materialized view table_name to name engine=xxx populate as select …
支持表引擎,如果源表被写入新数据,那么物化视图也会同步更新。populate修饰符觉得初始化策略,是否连带源表中已存在的数据一并导入。
物化视图本质上是一张特殊的数据表
目前只有MergeTree、Merge、Distribute三类表引擎支持ALTER查询
RENAME可以用于移动数据表,仅限单个节点范围内
分布式DDL,只需要在DDL后面加上 ON CLUSTER cluster_name
数据写入方式:
常规:INSERT INTO table_name VALUES (xxx)
指定格式:INSERT INTO table_name FORMAT format_name data_set
SELECT子语句:INSERT INTO table_name SELECT …
数据删除与修改
ClickHouse的Delete和Update能力被成为Mutation查询,它可以看做ALTER语句的变种,是一种很重的操作,不支持事务,执行是异步的后台过程,语句提交之后就会立即返回,执行进度需要通过system.mutaitons系统表查询。每执行一条ALTER DELETE语句,都会在mutations系统表中生成一条对应的执行计划。数据删除过程是以数据表的每个分区目录为单位,将所有目录重写为新的目录,新目录的命名规则是在原有名称上加上system.mutaitions.block_numbers.number。数据在重写过程中会将需要删除的数据去掉。旧的数据目录不会立即删除,而是会被标记成非激活状。等到MergeTree引擎下次合并动作触发时,这些非激活目录才会真正从物理意义上删除。
ClickHouse多种实时更新方法总结:https://www.modb.pro/db/197765
ClickHouse拥有内置和扩展两类数据字典,目前内置的只有YM字典,扩展的有7种类型,其中flat、hashed、range_hashed依次拥有最高的性能。
第三部分,原理篇
分区规则,分区目录合并规则
一级索引,稀疏索引
二级索引,跳数索引
如果把MergeTree比作一本书,primary.idx一级索引就好比这本书的一级章节目录,.bin(数据存储文件)中的数据就好比这本书中的文字,.mrk(数据标记文件)会为一级章节目录和具体的文字之间建立关联。
数据TTL,分为列级别和表级别,默认合并频率1天,可以通过merge_with_ttl_timeout来控制
多路径存储策略:默认,JBOD,HOT/COLD
ReplacingMergeTree处理逻辑
1、使用ORDER BY排序键作为判断重复数据的逻辑
2、只有在合并分区的时候才会触发删除重复数据的逻辑
3、以数据分区为单位删除重复数据。当分区合并时,同一分区内的重复数据才会被删除;不同分区之间的重复数据不会被删除
4、在进行数据去重时,因为分区内的数据已经基于ORDER BY进行排序,所以能够找到那些相邻的重复数据
5、数据去重策略有两种:
如果没有设置ver版本号,则保留同一组重复数据中的最后一行
如果设置了ver版本号,则保留同一组重复数据中ver字段取值最大的那一行
如果同时声明了ORDER BY和PRIMARY KEY,MergeTree会强制要求PRIMARY KEY列字段必须是ORDER BY的前缀
SummingMergeTree处理逻辑
1、用ORDER BY排序键作为聚合数据的条件Key
2、只有在合并分区的时候才会触发汇总的逻辑
3、以数据分区为单位来聚合数据。当分区合并时,同一数据分区内聚合Key相同的数据会被合并汇总,而不同分区之间的数据则不会被汇总
4、如果在定义引擎时指定了columns汇总列(非主键的数值类型字段),则SUM汇总这些列字段,如果未指定,则聚合所有非主键的数值类型字段
5、在进行数据汇总时,同一分区内,相同聚合Key的多行数据会合并成一行。其中,汇总字段会进行SUM计算,对于那些非汇总字段,则会使用第一行数据的取值
6、支持嵌套结构,但列字段名称必须以Map后缀结尾。嵌套类型中,默认以第一个字段作为聚合Key。除第一个字段外,任何名称以Key、Id或Type为后缀结尾的字段,都将和第一个字段组成复合Key
AggregatingMergeTree处理逻辑
1、用ORDER BY排序键作为聚合数据的条件Key
2、使用AggregateFunction字段类型定义聚合函数的类型以及聚合的字段
3、只有在合并分区的时候才会触发汇总的逻辑
4、以数据分区为单位来聚合数据。当分区合并时,同一数据分区内聚合Key相同的数据会被合并汇总,而不同分区之间的数据则不会被汇总
5、在进行数据计算时,因为分区内的数据已经基于ORDER BY进行排序,所以能够找到那些相邻且拥有相同聚合Key的数据
6、在聚合数据时,同一分区内,相同聚合Key的多行数据会合并成一行。对于那些非主键、非AggregateFunction类型字段,则会使用第一行数据的取值
7、AggregateFunction类型的字段使用二进制存储,在写入数据时,需要调用State函数;而在查询数据时,则需要调用相应的Merge函数。其中,*表示定义时使用的聚合函数
8、AggregatingMergeTree通常作为物化视图的表引擎,与普通MergeTree搭配使用
另外还有CollapsingMergeTree、VersionedCollapsingMergeTree、GraphiteMergeTree,以及整个MergeTree家族引擎的关系
其他常见表引擎
外部存储类型:HDFS、MySQL、JDBC、Kafka、File
内存类型:Memory、Set、Join、Buffer
日志类型:TinyLog、StripeLog、Log
接口类型:Merge、Dictionary、Distributed
其他类型:Live View、Null、URL
对于列式存储数据库,应尽量避免使用select *查询
ClickHouse支持的查询语法
1 | [WITH expr_list|(subquery)] |
WITH:定义变量、调用函数、定义子查询、在子查询中重复使用WITH
FROM:
SAMPLE:数据采样
ARRAY JOIN:支持INSERT和LEFT两种JOIN策略,同时对多个数组字段进行ARRAY JON操作时,查询的计算逻辑是按行合并而不是产生笛卡尔积
JOIN:按精度可以分为ALL、ANY、ASOF三种,按类型可以分为外连接、内连接、交叉连接三种
WHERE和PREWHERE:PREWHERE只能用于MergeTree系列引擎,它首先会读取PREWEHRE指定的列字段,用于数据过滤的条件判断。待数据过滤后再读取SELECT声明的列字段以补全其余属性。性能更高。
GROUP BY:SELECT如果声明了列字段,则只能使用聚合键包含的字段。WITH ROLLUP,WITH CUBE,WITH TOTALS三种额外汇总信息
HAVING:需要和GROUP BY同时使用,不能单独使用。在聚合计算之后实现二次过滤数据
ORDER BY:全局排序,可以通过NULL FIRST/LAST修饰NULL值的排序
LIMIT BY:和LIMIT不同,运行于ORDER BY之后和LIMIT之前,能够按指定分组,最多返回前n行数据,常用于TOP N的查询场景
LIMIT:
SELCT:
DISTINCT:
UNION ALL:联合左右两边的两组子查询,将结果一并返回。
Replicated前缀表引擎才能应用副本的能力,增加了zk监听的部分,通过zk存储mutaions操作日志
Distributed表引擎,本身不存储任何数据,它能够作为分布式表的一层透明代理。
多副本的路由规则,四种负载均衡算法:random、nearest_hostname,in_order、first_or_random
多分片查询的核心流程:查询各个分片数据,合并返回结果
使用Global优化分布式查询
服务监控可以从两个方面入手:
系统表:metrics、events、asynchronous_metrics
查询日志:query_log、query_thread_log、part_log、text_log、metric_log