一、参考资料
1、官方文档
二、特点
数据压缩
列式存储
-
分布式查询
数据可以在不同的分片上保存,查询可以在所有分片上并行处理
支持索引
列向量引擎,高效利用cpu资源
支持近似计算加速查询
三、引擎
MergeTree
-
简介
- 执行高负载任务的最通用和最强大的表引擎
- 数据总会以数据片段的形式写入磁盘,且数据片段不可修改
- 后台线程定时将数据片段合并
-
特点
- 存储的数据按照主键排序:允许创建稀疏索引,从而加快数据查询速度
- 支持分区,可以通过PARTITION BY语句指定分区字段
- 支持数据副本
- 支持数据采样
-
MergeTree表引擎
建表语法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2], ... INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1, INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2 ) ENGINE = MergeTree() ORDER BY expr [PARTITION BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...] [SETTINGS name=value, ...]
ENGINE:ENGINE = MergeTree(),MergeTree引擎没有参数
ORDER BY:排序字段。比如ORDER BY (Col1, Col2),值得注意的是,如果没有指定主键,默认情况下 sorting key(排序字段)即为主键。如果不需要排序,则可以使用ORDER BY tuple()语法,这样的话,创建的表也就不包含主键。这种情况下,ClickHouse会按照插入的顺序存储数据。必选。
PARTITION BY:分区字段,可选。
PRIMARY KEY:指定主键,如果排序字段与主键不一致,可以单独指定主键字段。否则默认主键是排序字段。可选。
SAMPLE BY:采样字段,如果指定了该字段,那么主键中也必须包含该字段。比如SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))。可选。
TTL:数据的存活时间。在MergeTree中,可以为某个列字段或整张表设置TTL。当时间到达时,如果是列字段级别的TTL,则会删除这一列的数据;如果是表级别的TTL,则会删除整张表的数据。可选。
SETTINGS:额外的参数配置。可选。
-
ReplacingMergeTree表引擎
-
建表语法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
-
去重
-
判断去重:
ReplacingMergeTree在去除重复数据时,是以ORDERBY排序键为基准的,而不是PRIMARY KEY
-
何时删除重复数据
执行分区合并时,会触发删除重复数据,有台线程自动删除,也可以手动合并,手动合并代码
optimize table_name final
不同分区的重复数据不会被去重
-
去重策略
如果没有设置[ver]版本号,则保留同一组重复数据中的最新插入的数据;如果设置了[ver]版本号,则保留同一组重复数据中ver字段取值最大的那一行
-
optimize命令使用
数据量大的情况下尽量不要使用,因为要消耗大量时间
-
-
-
SummingMergeTree表引擎
-
建表语法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = SummingMergeTree([columns]) -- 指定合并汇总字段 [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
把具有相同主键的行合并成一行,该行包含了被合并的行中具有数值数据类型的列的汇总值
如果未指定聚合字段,会按照非主键的数值类型字段进行聚合
未指定的聚合字段,则会保留最早的那一条
-
-
Aggregatingmergetree表引擎
通过预先定义的聚合函数计算数据,可以指定各种的聚合函数
-
CollapsingMergeTree表引擎
-
建表语法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = CollapsingMergeTree(sign) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
以增代删,支持行级数据修改和删除的表引擎
通过定义一个sign标记位字段,记录数据行的状态。如果sign标记为1,则表示这是一行有效的数据;如果sign标记为-1,则表示这行数据需要被删除。当CollapsingMergeTree分区合并时,同一数据分区内,sign标记为1和-1的一组数据会被抵消删除
-
限制
- 数据写入有严格的顺序,否则可能导致无法正常折叠
- 单线程执行可以较好控制数据写入顺序,多线程不能保证
-
-
VersionedCollapsingMergeTree表引擎
-
建表语法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = VersionedCollapsingMergeTree(sign, version) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
解决CollapsingMergeTree表引擎数据乱序不能折叠的问题
建表除了需要指定一个sign标识之外,还需要指定一个UInt8类型的version版本号
-
Log引擎
-
场景
主要用于快速写入小表(1百万行左右的表),然后全部读出的场景。即一次写入多次查询
-
特点
- 数据存储在磁盘上,写入数据时直接追加
- 不支持并发读写,当表写入时,会阻塞表的读取查询
- 不支持原子写,当写入中断时,可能会获得带有损坏数据的表
- 不支持索引
- 不支持alter操作,例如update、delete
-
TinyLog表引擎
-
建表语法
CREATE TABLE testlog ( column_name Uint16 comment '列' ... )ENGINE=TinyLog();
由每一列的数据文件和一个元数据文件组成
不支持并发读数据
-
-
StripLog表引擎
- 支持并行查询
- 所有列数据存储在同一个文件中,减少了文件的使用数量
- 由3个文件组成
- data.bin数据文件,保存所有列的数据
- .mrk标记文件,保存了数据在data.bin文件中的位置信息,便于多线程查找数据,提高查询效率
- size.json元数据文件,记录其余两个文件的大小
-
Log引擎表
适用于临时数据,一次性写入,结合以上两个表引擎的优点,是Log系列引擎中性能最高的表引擎
集成引擎
与其它数据存储以及处理系统集成的引擎,如 Kafka,MySQL 以及 HDFS
-
kafka引擎
-
建表语法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = Kafka() SETTINGS kafka_broker_list = 'host:port', kafka_topic_list = 'topic1,topic2,...', kafka_group_name = 'group_name', kafka_format = 'data_format'[,] [kafka_row_delimiter = 'delimiter_symbol',] [kafka_schema = '',] [kafka_num_consumers = N,] [kafka_max_block_size = 0,] [kafka_skip_broken_messages = N,] [kafka_commit_every_batch = 0,] [kafka_thread_per_consumer = 0]
-
注意
一旦查询完毕之后,ClickHouse会删除表内的数据,其实Kafka表引擎只是一个数据管道,可以通过物化视图的方式访问Kafka中的数据
-
开发流程
一般需要三张表:
kafka表,负责描述topic,消费数据和解析数据
MergeTree表,存kafka数据
-
物化视图,用来把kafka表和MergeTree表关联起来
create materialized view test_view to merge_tree_table as select * from kafka_table
-
-
特殊引擎
用于一些特定的功能,如 Distributed 用于分布式查询,MaterializedView 用来聚合数据,以及 Dictionary 用来查询字典数据等
例如Distributed表引擎是分布式表的代名词,它自身不存储任何数据,数据都分散存储在某一个分片上,能够自动路由数据至集群中的各个节点,所以Distributed表引擎需要和其他数据表引擎一起协同工作
创建分布式表:
CREATE TABLE IF NOT EXISTS table_dist ON CLUSTER cluster_name ( column1 Int32, column2 String )ENGINE = Distributed(cluster_name, db_name, local_table_name [,sharding_key]); /* cluster_name:集群名称,与集群配置中的自定义名称相对应。 db_name:数据库名称 local_table_name:表名称 sharding_key:可选的,用于分片的key值,在数据写入的过程中,分布式表会依据分片key的规则,将数据分布到各个节点的本地表。 */
创建分布式表是读时检查的机制,也就是说对创建分布式表和本地表的顺序并没有强制要求
接着就可以在每台机器上看到刚刚创建的分布式表了,然后可以在每台机器上创建一张本地表该分布式表映射的本地表了
数据副本
数据副本可以查看这里
四、开发
-
常见问题
-
数据插入报错 too many parts exception
原因:数据写入过于频繁,导致后台merge分片速度缓慢,ch拒绝数据写入
解决:可以增加每次数据写入的batch_size(例如10万),或者增大写入数据的间隔时间
-
复制表变为只读
原因:ClickHouse 无法连接 ZooKeeper 集群或 ZooKeeper 上该复制表的元数据丢失导致的,此时新数据无法插入该表
-
执行 JOIN 操作时内存超限
原因:没有进行数据过滤,或者join本身的数据量很大
解决:修改配置文件中的内存限制
-
直接写入方式
- 写入分布式表
- 先放入本次磁盘的缓冲区,然后再分发给所有节点的MergeTree表
- 风险
- 在分发给MergeTree表时节点宕机,会造成数据丢失
- 在节点宕机恢复后重新分发数据,可能会造成数据重复写入
- 分发生成更多的小文件,拖垮系统
- 写入MergeTree表
- 解决数据分发问题,但依然扛不住高频的数据写入
- 需要控制好写入数量和写入时间间隔,不然可能会造成后台合并数据分片的速度会慢于数据写入数据,造成数据写入拒绝
- 写入分布式表
-