clickhouse的一些总结

应用场景:

  1. 绝大多数请求都是用于读访问的
  2. 数据需要以大批次(大于1000行)进行更新,而不是单行更新;或者根本没有更新操作
  3. 数据只是添加到数据库,没有必要修改
  4. 读取数据时,会从数据库中提取出大量的行,但只用到一小部分列
  5. 表很“宽”,即表中包含大量的列
  6. 查询频率相对较低(通常每台服务器每秒查询数百次或更少)
  7. 对于简单查询,允许大约50毫秒的延迟
  8. 列的值是比较小的数值和短字符串(例如,每个URL只有60个字节)
  9. 在处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行)
  10. 不需要事务
  11. 数据一致性要求较低
  12. 每次查询中只会查询一个大表。除了一个大表,其余都是小表
  13. 查询结果显著小于数据源。即数据有过滤或聚合。返回结果不超过单个服务器内存大小

ClickHouse本身的限制:

  1. 不支持真正的删除/更新支持 不支持事务(期待后续版本支持)
  2. 不支持二级索引
  3. 有限的SQL支持,join实现与众不同
  4. 不支持窗口功能
  5. 元数据管理需要人工干预维护

ClickHouse有多快

ClickHouse底层设计

数据表引擎

表引擎(即表的类型)决定了:

  • 数据的存储方式和位置,写到哪里以及从哪里读取数据
  • 支持哪些查询以及如何支持。
  • 并发数据访问。
  • 索引的使用(如果存在)。
  • 是否可以执行多线程请求。
  • 数据复制参数。

MergeTree

  • MergeTree
  • ReplacingMergeTree
  • SummingMergeTree
  • AggregatingMergeTree
  • CollapsingMergeTree
  • VersionedCollapsingMergeTree
  • GraphiteMergeTree

最小功能的轻量级引擎

  • TinyLog
  • StripeLog
  • Log

与其他的数据存储与处理系统集成的引擎

  • Kafka
  • MySQL
  • HDFS

其他特定功能的引擎

  • Distributed
  • MaterializedView
  • Dictionary
  • Merge
  • File
  • Null
  • Set
  • Join
  • URL
  • View
  • Memory
  • Buffer

clickhouse为什么如此快

  • 优秀的执行引擎以及存储引擎,clickhouse是基于列式存储的,使用了向量化的执行引擎
  • 稀疏索引,clickhouse不仅有基于分区的过滤,还有基于列级别的稀疏索引,这样在进行条件查询的时候可以过滤到很多不需要扫描的块
  • 存储执行耦合,避免了网络的开销,CPU的极致压榨加上SSD的加持,每秒的数据传输对于网络带宽的压力是非常大的

clickhouse如何完成一次完整的select

  • clickhouse的表分为两种,本地表和分布式表
  • 分布式表是一个逻辑上的表,不存储数据的只是做一个路由使用,一般在查询的时候都是直接使用分布式表,分布式表引擎会将我们的查询请求路由本地表进行查询,然后进行汇总最终返回给用户

clickhouse如何完成一次插入

  • clickhouse的插入是批量的,它不能够像传统的mysql那样频繁的单条记录插入,没有内存表,不进行日志的记录。
  • clickhouse写入的时候是直接落盘的, 在落盘之前会对数据进行排序以及必要的拆分(如不同分区的数据会拆分成多个文件夹),如果使用的是ReplicatedMergeTree引擎还需要与zookeeper进行交互,最终会有线程在后台把数据(文件夹)进行合并(merge),将小文件夹合并生成大文件夹方便查询的时候进行读取(小文件会影响查询性能)。

clickhouse支持分区的过滤和列级别的稀疏索引

  • 传统基于hdfs的olap引擎都是不支持索引的,基本的数据过滤只能支持分区进行过滤,这样会扫描处理很多不必要的数据。
  • clickhouse的基础索引是使用了和kafka一样的稀疏索引,索引粒度默认是8192,即每8192条数据进行一次记录,这样对于1亿的数据只需要记录12207条记录,二分查找+遍历也可以快速的索引到指定的数据

常见的引擎(MergeTree家族)

  1. (Replicated)MergeTree:该引擎为最简单的引擎,存储最原始数据不做任何的预计算,任何在该引擎上的select语句都是在原始数据上进行操作的,常规场景使用最为广泛,其他引擎都是该引擎的一个变种。
  2. (Replicated)SummingMergeTree:该引擎拥有“预计算(加法)”的功能。实现原理:在merge阶段把数据加起来(对于需要加的列需要在建表的时候进行指定),对于不可加的列,会取一个最先出现的值。
  3. (Replicated)ReplacingMergeTree:该引擎拥有“处理重复数据”的功能。使用场景:“最新值”,“实时数据”。
  4. (Replicated)AggregatingMergeTree:该引擎拥有“预聚合”的功能。使用场景:配合”物化视图”来一起使用,拥有毫秒级计算UV和PV的能力。
  5. (Replicated)CollapsingMergeTree:该引擎和ReplacingMergeTree的功能有点类似,就是通过一个sign位去除重复数据的。需要注意的是,上述所有拥有"预聚合"能力的引擎都在"Merge"过程中实现的,所以在表上进行查询的时候SQL是需要进行特殊处理的。如SummingMergeTree引擎需要自己sum(), ReplacingMergeTree引擎需要使用时间+版本进行order by + limit来取到最新的值,由于数据做了预处理,数据量已经减少了很多,所以查询速度相对会快非常多。
  6. 推荐使用(*)MergeTree引擎,该引擎是clickhouse最核心的组件,也是社区优化的重点

最佳实践

  • 实时写入使用本地表,不要使用分布式表

数据导入

order by

order by是 MergeTree 中唯一一个必填项,甚至比 primary key 还重要,因为当用户不设置主键的情况,很多处理会依照 order by 的字段进行处理

MergeTree 提供了可以管理数据表或者列的生命周期的功能

  • 涉及判断的字段必须是 Date 或者 Datetime 类型,推荐使用分区的日期字段
  • 给列的加上生命周期到时间后会自动清除数据
  • 表的生命周期

ReplacingMergeTree

  • 和 MergeTree 的不同之处在于它会删除排序键值相同的重复项
  • 去重只会在分区内部进行去重,不能执行跨分区的去重。
  • 数据的去重只会在数据合并期间进行。合并会在后台一个不确定的时间进行,但是它不保证没有重复的数据出现。
  • 保存的数据为版本字段值最大的,如果版本字段相同则按插入顺序保留最后一个。

SummingMergeTree

  • 合并 SummingMergeTree 表的数据片段时,会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。
  • columns 包含了将要被汇总的列的列名的元组。可选参数。所选的列必须是数值类型,并且不可位于主键中。
  • 不再一个分区里的数据不会聚合
  • 可以填写多列必须数字列,如果不填,以所有非维度列且为数字列的字段为汇总数据列

MySQL和ClickHouse中数据类型的对应关系

MySQL ClickHouse
UNSIGNED TINYINT UInt8
TINYINT Int8
UNSIGNED SMALLINT UInt16
SMALLINT Int16
UNSIGNED INT, UNSIGNED MEDIUMINT UInt32
INT, MEDIUMINT Int32
UNSIGNED BIGINT UInt64
BIGINT Int64
FLOAT Float32
DOUBLE Float64
DATE Date
DATETIME, TIMESTAMP DateTime
BINARY FixedString

数据类型

  1. 整型:UInt8,UInt16,UInt32,UInt64,Int8,Int16,Int32,Int64 范围U开头-2N/2~2N-1;非U开头0~2^N-1
  2. 枚举类型:Enum8,Enum16 Enum(‘hello’=1,‘test’=-1),Enum是有符号的整型映射的,因此负数也是可以的
  3. 字符串型:FixedString(N),String N是最大字节数,不是字符长度,如果是UTF8字符串,那么就会占3个字节,GBK会占2字节;String可以用来替换VARCHAR,BLOB,CLOB等数据类型
  4. 时间类型:Date
  5. 数组类型:Array(T) T是一个基本类型,包括arry在内,官方不建议使用多维数组
  6. 元组:Tuple
  7. 结构:Nested(name1 Type1,name2 Type2,…) 类似一种map的结

物化列

DROP TABLE IF EXISTS test.m2;
CREATE TABLE test.m2 ( a MATERIALIZED ( b + 1 ), b UInt16 ) ENGINE = Memory;
INSERT INTO test.m2 ( b ) VALUES( 1 );
SELECT * FROM test.m2;
SELECT a,b FROM test.m2;

语法优化 EXPLAIN SYNTAX

EXPLAIN SYNTAX SELECT UserID FROM hits_test GROUP BY UserID HAVING UserID =
'8585742290196126178';

数据采样

SELECT Title,count(*) AS PageViews 
FROM hits_v1
SAMPLE 0.1 
WHERE CounterID =57
GROUP BY Title
ORDER BY PageViews DESC LIMIT 100;

避免构建虚拟列

尽量别用as创建一列新列,例如 select a/b as t from test

物化视图

  • 物化视图是把查询的结果和相应的引擎存入到磁盘或内存中,类似形成了一张新的表,但是可以对表中数据进行重新组织.
  • 使用物化视图好处,可以避免对基础表的频繁查询并复用结果,从而显著提升查询的性能
CREATE MATERIALIZED VIEW hits_mv 
ENGINE=SummingMergeTree
PARTITION BY toYYYYMM(EventDate) ORDER BY (EventDate, intHash32(UserID)) 
AS SELECT
UserID,
EventDate,
count(URL) as ClickCount,
sum(Income) AS IncomeSum
FROM hits_test
WHERE EventDate >= '2022-03-20'
GROUP BY UserID,EventDate;

ClickHouse实时同步MySQL数据的两种方式

  1. 使用JDBC方式同步

    • 使用Canal组件完成binlog的解析和数据同步;
    • Canal-Server进程会伪装成MySQL的slave,使用MySQL的binlog同步协议完成数据同步;
    • Canal-Adapter进程负责从canal-server获取解析后的binlog,并且通过jdbc接口写入到ClickHouse; 优点:Canal组件原生支持;缺点:Canal-Adpater写入时源表和目的表一一对应,灵活性不足;需要维护两个Canal组件进程;
  2. Kafka+ClickHouse物化视图方式同步

    • Canal-Server完成binlog的解析,并且将解析后的json写入Kafka;
    • Canal-Server可以根据正则表达式过滤数据库和表名,并且根据规则写入Kafka的topic;
    • ClickHouse使用KafkaEngine和Materialized View完成消息消费,并写入本地表; 需要维护Kafka和配置规则;ClickHouse需要新建相关的视图、Kafka Engine的外表等;

参考地址

版权

本作品采用 CC BY-NC-ND 4.0 授权,转载必须注明作者和本文链接。