前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >初探MergeTree的WAL功能

初探MergeTree的WAL功能

作者头像
Nauu
发布2020-11-03 11:12:23
1.6K0
发布2020-11-03 11:12:23
举报
文章被收录于专栏:ClickHouse的秘密基地

ClickHouse 最近在 MergeTree 中加入了 WAL 预写日志的能力,这无疑又加强了MergeTree的实力,该功能目前已经被合并到主线。

在介绍 WAL 之前,我们首先重温一下 MergeTree 最基本的合并过程。

新建一张 MergeTree:

代码语言:javascript
复制
CREATE TABLE test20(
  ID String,
  Price Int32,
  Val Float64,
  EventTime Date
) engine = MergeTree()
PARTITION BY toYYYYMM(EventTime)
ORDER BY ID

随后依次写入 3 行数据:

代码语言:javascript
复制
INSERT INTO test20 VALUES('id1',100,50.5,'2020-05-06')
INSERT INTO test20 VALUES('id2',200,50.5,'2020-05-06')
INSERT INTO test20 VALUES('id3',200,50.5,'2020-05-06')

此时,MergeTree 会生成 3 个分区目录:

代码语言:javascript
复制
total 8
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_1_1_0
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_2_2_0
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_3_3_0
drwxr-xr-x   2 nauu  staff   64 10 14 21:56 detached
-rw-r-----   1 nauu  staff    1 10 14 21:56 format_version.txt

在合并之后,会生成一个新的合并分区 202005_1_3_1:

代码语言:javascript
复制
total 8
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_1_1_0
drwxr-xr-x  16 nauu  staff  512 10 14 21:01 202005_1_3_1
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_2_2_0
drwxr-xr-x  16 nauu  staff  512 10 14 21:56 202005_3_3_0
drwxr-xr-x   2 nauu  staff   64 10 14 21:56 detached
-rw-r-----   1 nauu  staff    1 10 14 21:56 format_version.txt

如果你还不理解分区目录的创建原理,可以看看这篇文章 一分钟视频解读ClickHouse MergeTree,或者更体系的看看《ClickHouse原理解析与应用实践》的第六章。

简而言之,只要执行一次 INSERT 语句,MergeTree 就会创建一次分区目录。也正因这个原因,当写入并发过高的时候,就会时常会看到我们的老朋友:

代码语言:javascript
复制
Too many parts (N). Merges areprocessing significantly slower than inserts.

WAL预写日志解决了这个问题。在 ClickHouse 的新版本中,MergeTree 多了这么几个参数:

代码语言:javascript
复制
    M(SettingUInt64, min_bytes_for_wide_part, 0, "Minimal uncompressed size in bytes to create part in wide format instead of compact", 0) \
    M(SettingUInt64, min_rows_for_wide_part, 0, "Minimal number of rows to create part in wide format instead of compact", 0) \
    M(SettingUInt64, min_bytes_for_compact_part, 0, "Experimental. Minimal uncompressed size in bytes to create part in compact format instead of saving it in RAM", 0) \
    M(SettingUInt64, min_rows_for_compact_part, 0, "Experimental. Minimal number of rows to create part in compact format instead of saving it in RAM", 0) \
    M(SettingBool, in_memory_parts_enable_wal, true, "Whether to write blocks in Native format to write-ahead-log before creation in-memory part", 0) \
    M(SettingUInt64, write_ahead_log_max_bytes, 1024 * 1024 * 1024, "Rotate WAL, if it exceeds that amount of bytes", 0) \

其中 in_memory_parts_enable_wal 默认为 true,这说明预写日志默认就是开启状态的。

所以现在 MergeTree 的写入流程发生了一些变化,分区目录首先会在内存中,为了保证内存中的数据不会丢失,也会同步的在WAL日志中写一份。当数据满足阈值条件时,再将数据刷到磁盘。

这么说起来比较抽象,举个例子,还是用刚才那张 MergeTree , 现在给它添加一个 min_rows_for_compact_part 参数:

代码语言:javascript
复制
CREATE TABLE test20(
  ID String,
  Price Int32,
  Val Float64,
  EventTime Date
) engine = MergeTree()
PARTITION BY toYYYYMM(EventTime)
ORDER BY ID
settings min_rows_for_compact_part = 2

min_rows_for_compact_part = 2 的含义表示,数据首先会被写到内存和 WAL中,当触发 Merge 的时候,如果数据大于 2 行,就直接把合并后的分区写到磁盘。

例如现在还是写入 3 行数据:

代码语言:javascript
复制
INSERT INTO test20 VALUES('id1',100,50.5,'2020-05-06')
INSERT INTO test20 VALUES('id2',200,50.5,'2020-05-06')
INSERT INTO test20 VALUES('id3',200,50.5,'2020-05-06')

因为写入之后还没有触发 Merge 动作,此时我们再去看磁盘目录:

代码语言:javascript
复制
drwxr-xr-x  2 nauu  staff   64 10 14 22:16 detached
-rw-r-----  1 nauu  staff    1 10 14 22:16 format_version.txt
-rw-r-----  1 nauu  staff  252 10 14 22:16 wal.bin

你会发现 MergeTree 并没有创建分区目录,而是多了一个 wal. bin 日志文件。

此时如果我们触发 Merge:

代码语言:javascript
复制
optimize TABLE test20

你会看到 MergeTree 直接通过预写日志创建了合并后的分区 202005_1_3_1

代码语言:javascript
复制
drwxr-xr-x  16 nauu  staff  512 10 14 22:17 202005_1_3_1
drwxr-xr-x   2 nauu  staff   64 10 14 22:16 detached
-rw-r-----   1 nauu  staff    1 10 14 22:16 format_version.txt
-rw-r-----   1 nauu  staff  252 10 14 22:16 wal.bin

这就是 MergeTree 在拥有了 WAL 日志后的写入过程。

除此之外,MergeTree 现在也扩充了分区的存储布局,在此之前,MergeTree 只有一种 wide 布局。也就是每个列字段都拥有一组独立的文件(1个bin文件和1个mark文件),例如下图所示:

那么现在,MergeTree 还提供了一种 compact 布局,例如我们在刚才的例子中再添加一个 min_rows_for_wide_part 参数:

代码语言:javascript
复制
CREATE TABLE test20(
  ID String,
  Price Int32,
  Val Float64,
  EventTime Date
) engine = MergeTree()
PARTITION BY toYYYYMM(EventTime)
ORDER BY ID
settings min_rows_for_compact_part = 2,
min_rows_for_wide_part = 10

min_rows_for_wide_part = 10 表示,如果数据大于 10 行,分区就是用 wide模式,否则就使用 compact 模式。

在这个用例中,我们的数据只有 3 行,所以当生成分区目录时,你会看到下图的情况:

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ClickHouse的秘密基地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档