1、MergeTree
1.1 TTL
- 存在列级TTL、表级TTL。
TTL create_time + INTERVAL 10 SECOND
- 写入数据时,会以数据分区为单位,在每个分区目录内生成一个名为ttl.txt的文件。
- 每当写入一批数据时,都会基于INTERVAL计算结果为分区生成ttl.txt。
- 只有在MergeTree合并分区时,才触发TTL过期逻辑。
- 规则是找过期早,合并次数多的。
1.2 多路径存储
- 支持以数据分区为最小移动单元,将分区目录写入多块磁盘目录。
- JBOD 服务器挂在多块磁盘,没有RAID,每执行一次INSERT或者MERGE,所产生的新分区会轮训写入各个磁盘。
- HOT/COLD。数据在写入之初回在HOT区域创建并保存,当分区数据大小累积到阈值,会自行移动到COLD区域。
2、ReplacingMergeTree
- MergeTree的主键没有唯一键约束。ReplacingMergeTree能够在合并分区时删除重复数据。
- 以ORDER BY 键为准,不是PRIMARY KEY。以分区为单位删除重复数据。
- 如果有ver则保留ver最大的,否则保留最后一行。
3、SummingMergeTree
- 在只关心数据汇总结果,不关心明细数据时。
- 能够在合并分区时按照预先定义的条件聚合汇总数据。
- 通常只有在使用SummingMergeTree AggregatingMergeTree时,ORDER BY和PRIMRY KEY不一样,这些引擎都是按照ORDER BY聚合,主键与聚合条件定义分离,为修改聚合条件留下空间。
- 但是PRIMARY KEY必须是ORDER BY前缀。
- 如果有嵌套类型,会按嵌套类型中的第一个字段作为聚合条件。
- 只在合并分区时触发。
4、AggregatingMergeTree
- 能够以二进制存储中间状态结果
- code和value是聚合字段,语义等同于UNIQ(code) SUM(value)
CREATE TABLE xx (
id String,
city String,
code AggregateFunction(uniq, String),
value AggregateFunction(sum, UInt32),
create_time DateTime
) ENGINE = AggregatingMergeTree()
PARTITION BY
ORDER BY
- 插入时要写
uniqueState('') sumState('')
- 查询时要写
uniqueMerge(code) sumMerge(value)
- 常结合物化视图使用,在底层有一个MergeTree更新就更方便。
- 也是只有分区才会触发聚合逻辑,聚合key是ORDER BY制定的。
5、CollapsingMergeTree
- 以增代删的思路,支持行级数据修改和删除的表引擎。它通过定义一个sign标记位字段,记录数据行状态。如果是-1就是需要被删除的,同一分区内1和-1的sign会抵消。
CollapsingMergeTree
- 如果-1和1一样多,并且最后一行是1,则保留第一行-1和最后一行1
- 如果-1和1一样多,并且最后一行是-1则都不保留。
6、VersionedCollapsingMergeTree
CREATE TABLE ver_coll (
id String,
code Int32,
ver UInt
) ENGINE = VersionedCollapsingMergeTree(ver)
- 会自动将ver作为排序条件并增加到ORDER BY的末端。
7、各种MergeTree之间的关系总结
- 其他6个变种表引擎的Merge合并逻辑,全部是建立在MergeTree基础之上的,且均继承于MergingSortedBlockInputStream
- 区分知识Merge逻辑,所以特殊功能只在Merge合并时才会触发。
逻辑区分
ReplicatedMergeTree
- 通过Zookeeper消息日志广播,实现副本实例之间的数据同步功能。
8、外部存储类型
- 直接从其他存储系统读取数据,表引擎只负责元数据管理和数据查询,通常不负责数据写入
- 包括HDFS、Mysql、JDBC、Kafka、File等类型。JDBC表引擎无法单独完成所有工作,需要依赖名为clickhouse-jdbc-bridge的查询代理服务。File表引擎的数据文件智能保存在config.xml配置中由path制定的路径下。
9、 内存
- Memory,重启就会消失
- Set,首先会被写内存,然后被同步到磁盘。不能直接使用select查,只能简介作为IN查询的右侧条件被查询使用。带有去重能力。
- Join,和set类似,但是可以作为Join的链接表,支持ALL ANY ASOF三种类型。ALL会忽略join_key的重复。
CREATE TABLE id_join(
id UInt8,
price UInt32,
time Datetime
) ENGINE = Join(ANY, LEFT, id)
SELECT id,name,price from xx LEFT JOIN id_join USING(id)
- Buffer,不是为了查询而设计的,是用来充当缓冲区的,比如写入速度很大的时候。
Buffer
10、 日志
- 适用数据量很小、一次写入多次查询的场景。
- 均不支持索引、分区等高级特性;不支持并发读写,当针对一张日志表写入时,针对这张表的查询会被阻塞。
- Log,有[column].bin文件 __marks.mrk sizes.json几个文件,各列独立存储,可以并行查询、能够按列按需读取
11、 其他类型
- Merge,负责合并多个查询的结果集。比如有的表是按年存的,可以用一个merge替代多张表
- Live View 不是表引擎,类似事件监听器。能够将一条SQL查询结果作为监控目标,当目标数据增加时,可以发出相应。
CREATE LIVE VIEW lv AS SELECT count(*) FROM xxx
会按递增版本显示监听的内容。 - Null,如果使用物化视图,不希望保留源表数据,可以把源表设置成null。
12、查询
12.1 SAMPLE
- 幂等设计,数据不发生变化,相同规则获取相同数据。
- SAMPLE BY声明的表达式必须同时包含在主键的声明内,Sample Key必须是Int类型
12.2 Array Join
- 将一个数组展开为多行 类似lateval view + explode
- 对多个数组字段进行Array Join,逻辑是按行合并不是产生笛卡尔积。如果多个数组同时Array Join,同一行里的数组长度需要一样。
12.3 Join
- ALL就是正常逻辑下理解的Join
- ANY,如果左表一行,右表多个匹配,只返回第一个。
- ASOF,模糊链接,asof_column必须是整型、浮点型和日期这类有序序列的数据类型。
SELECT xx FROM a ASOF INNER JOIN b ON a.id = b.id AND a.time = b.time
// 等同于a.time > b.time
- 应遵循左大右小的原则,目前没有缓存支持。
12.4 GRPOUP BY
- WITH ROLLUP
- WITH CUBE
- WITH TOTALS 基于聚合函数对所有数据进行统计。但是只聚合在对应的group by粒度。
12.5 HAVING
- 在clickhouse里 嵌套WHERE效率会更高,因为会有谓词下推。
- HAVING是在聚合之后增加了filter
12.6 LIMIT BY
- 能够按照指定分组,最多返回前n行数据
13、查询计划
clickhouse-client -h localhost --send_logs_level=trace <<< '执行语句' > /dev/null
查询计划
- Key condition
- MinMax index condition
- 分区目录数
- 扫描数据行数
- 消耗多少内存
- 具体的执行计划树。