ClickHouse 介绍
ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式数据库,使用 C++ 语言编写,主要用于在线分析处理(OLAP)。
一、特点
1.1 列式存储
以下面表为例:


好处是想查找某个人所有属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。

这时想查找所有人的年龄,只需要把年龄那一列拿出来就可以了。

- 对于列的聚合、计数、求和等统计操作优于行式存储。
- 由于一列的数据类型是相同的,针对数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比。
- 由于数据压缩比更好,一方面节省了磁盘空间,另一方面对于 cache 也有了更大的发挥空间。
1.2 多样化引擎
ClickHouse 把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。
1.3 高吞吐写入能力
ClickHouse 采用类 LSM Tree 的结构,数据写入后定期在后台 Compaction。通过类 LSM Tree 的结构,ClickHouse 在数据导入时全部是顺序 append 写,写入后数据段不可更改,在后台 compaction 时也是多个段 merge sort 后顺序写回磁盘。顺序写的特性,充分利用了磁盘的吞吐能力。
1.4 数据分区与线程级并行
ClickHouse 将数据划分为多个 partition,每个 partition 再进一步划分为多个 index granularity(索引粒度),然后通过多个 CPU 核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条查找就能利用整机所有 CPU,但这样就不利于同时并发多条查询,所以对于高并发查询业务,ClickHouse 并不是强项。
二、表引擎
2.1 TinyLog
以列文件的形式保存在磁盘上,不支持索引,没有并发控制。一般保存少量数据的小表,生产环境基本不使用,主要用于平时练习测试用。
建表语句:
create table t_tinylog ( id String, name String) engine=TinyLog;2.2 Memory
内存引擎,数据以未压缩的原始形式直接保存在内存中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。一般用来测试。
2.3 MergeTree
ClickHouse 中最强大的表引擎当属 MergeTree 引擎及该系列(*MergeTree)
中的其他引擎,支持索引和分区。
建表语句:
create table t_order_mt(id UInt32,sku_id String,total_amount Decimal(16,2),create_time Datetime
) engine =MergeTree
partition by toYYYYMMDD(create_time)primary key (id)order by (id,sku_id);MergeTree 还有很多参数(绝大多数使用默认值即可),但是有三个参数是极其重要的:
2.3.1 partition by 分区(可选)
作用:
降低扫描的范围,优化查询速度。
如果不填:
只会使用一个分区。
分区目录:
MergeTree 是以列文件+索引文件+表定义文件组成的,如果设定了分区,那么这些文件就会保存到不同的分区目录中。
数据写入与分区合并:
任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。写入后的某个时刻,ClickHouse 会自动执行合并操作(也可手动通过 optimize 执行),把临时分区的数据合并到已有分区中。
optimize table tableName final;2.3.2 primary key 主键(可选)
ClickHouse 的主键和其他数据库不太一样,他只提供了数据的一级索引,但却不是唯一约束。这就意味着是可以存在相同主键的数据。主键的设定主要依据是查询语句中的 where 条件。根据条件通过对主键进行某种形式的二分查找,能够定位到对应的前后两个索引位置,避免了全表扫描。
2.3.3 order by 排序(必选)
order by 设定了分区内的数据按照哪些字段顺序进行有序保存。
order by 是 MergeTree 中唯一一个必填项,比主键还重要,因为当用户不设置主键的情况下,很多处理会按照 order by 的字段进行处理(比如去重和汇总)。
2.3.4 数据 TTL
- 列级别 TTL
在建表语句的字段定义位置设置。例如reate table t_order_mt2(id UInt32,sku_id String,total_amount Decimal(16,2) TTL create_time+interval 10 SECOND,create_time Datetime ) engine =MergeTree partition by toYYYYMMDD(create_time)primary key (id)order by (id, sku_id);到期后,指定的字段数据归0
- 表级别 TTL
alter table t_order_mt3 MODIFY TTL create_time + INTERVAL 10 SECOND;数据会在 create_time 之后 10 秒丢失。create_time 必须是 Date 或者 Datetime 类型,推荐使用分区的日期字段。
4.4 ReplacingMergeTree
ReplacingMergeTree 是 MergeTree 的一个变种,它存储特性完全继承 MergeTree,只是多了一个去重的功能。想要处理掉重复的数据,就可以借助这个表引擎。
去重时机:
数据的去重只会发生在合并的过程中。合并会在位置的时间在后台进行。
去重范围:
如果表经过了分区,去重只会发生在分区内部,不能执行跨分区的去重。
建表语句:
create table t_order_rmt(id UInt32,sku_id String,total_amount Decimal(16,2) ,create_time Datetime
) engine = ReplacingMergeTree(create_time)partition by toYYYYMMDD(create_time)primary key (id)order by (id, sku_id);ReplacingMergeTree() 填入的参数为版本字段,重复数据保留版本字段值最大的。如果不填版本字段,默认按照插入顺序保留最后一条。如果版本字段值相同,也按照插入顺序保留最后一笔。
4.5 SummingMergeTree
对于不关心查询明细,只关心以维度进行汇总聚合结果的场景,可以使用 SummingMergeTree ,它能够“预聚合”。
建表语句:
create table t_order_smt(id UInt32,sku_id String,total_amount Decimal(16,2) ,create_time Datetime
) engine =SummingMergeTree(total_amount)partition by toYYYYMMDD(create_time)primary key (id)order by (id,sku_id );- 以 SummingMergeTree() 中指定的列作为汇总数据列
- 可以填写多列,必须数字列,如果不填,以所有非维度列且为数字列的字段会汇总数据列
- 以 order by 的列为准,作为维度列
- 其他列按插入顺序保留第一行
- 不在一个分区的数据不会被聚合
