ClickHouse新版本开始支持Write-Ahead-Log(WAL) 功能。

介绍 WAL 之前,先重温一下 MergeTree 最基本的合并过程。
MergeTree的高频写问题?
CREATE TABLE test( id UInt8, name String, age UInt8, shijian Date)ENGINE = MergeTree()PARTITION BY toYYYYMM(shijian)ORDER BY id;insert into test values(1001,'张三','18',now());insert into test values(1002,'李四','28',now());insert into test values(1003,'王五','38',now());
此时,MergeTree 会生成 3 个分区目录:

手动合并之后的分区目录

对于 ClickHouse MergeTree 引擎,在数据的写入过程中,数据总会以数据片段的形式被写入磁盘,且数据片段不可修改。每一批次的写入(每执行一次INSERT)MergeTree都会按照分区规则在磁盘上生成一个全新的分区目录(part),为了避免片段过多,在未来的某一时刻,属于相同分区的数据片段会被合并成一个全新的分区目录,这种数据分区往复合并的特点正是合并树的名称由来。其中如果有多个客户端,每个客户端写入的数据量较少、次数较频繁的情况下,就会引发以下错误提示。
Too many parts (N). Merges areprocessing significantly slower than inserts.WAL预写日志解决了这个问题,提高写入性能,在 ClickHouse 的新版本中,MergeTree 多了这么几个参数:
M(SettingUInt64, min_bytes_for_wide_part, 0, xxxxxxxx, 0) \M(SettingUInt64, min_rows_for_wide_part, 0, xxxxxxxxx, 0) \M(SettingUInt64, min_bytes_for_compact_part, 0, xxxxx, 0) \M(SettingUInt64, min_rows_for_compact_part, 0, xxxxxx, 0) \M(SettingBool, in_memory_parts_enable_wal, true, xxxx, 0) \M(SettingUInt64, write_ahead_log_max_bytes, 1024 * 1024 * 1024, xxxx, 0) \
其中 in_memory_parts_enable_wal 默认为 true,这说明预写日志默认就是开启状态的。
CREATE TABLE default.test1( id UInt8, name String, age UInt8, shijian Date)ENGINE = MergeTree()PARTITION BY toYYYYMM(shijian)ORDER BY idSETTINGS min_rows_for_compact_part = 2, index_granularity = 8192;
min_rows_for_compact_part = 2 表示数据首先会被写到内存和 WAL中,当触发 Merge 的时候,如果数据大于 2 行,就直接把合并后的分区写到磁盘。
insert into test1 values(1001,'张三','18',now());insert into test1 values(1002,'李四','28',now());insert into test1 values(1003,'王五','38',now());
写入之后还没有触发 Merge 动作,磁盘目录情况:

clickhouse-client -moptimize table test1;

在此之前,MergeTree 只有一种 wide 布局,也就是每个列字段都拥有一组独立的文件,例如下图所示:

由于现在添加了wal新特性,致使MergeTree的分区布局也得到了扩展,插入过程中,数据首先进入内存,满足阈值之后,会将内存中的数据刷到磁盘。
CREATE TABLE default.test2( id UInt8, name String, age UInt8, shijian Date)ENGINE = MergeTree()PARTITION BY toYYYYMM(shijian)ORDER BY idSETTINGS min_rows_for_compact_part = 2, min_rows_for_wide_part = 10, index_granularity = 8192;
insert into test2 values(1001,'张三','18',now());insert into test2 values(1002,'李四','28',now());insert into test2 values(1003,'王五','38',now());optimize table test2;

所有的的数据写到了同一个 data.bin 文件中,所有列的标记文件也都写到了同一个.mark文件。当列字段很多,数据又很少的时候,可以考虑使用这种布局模式的分区。




