侧边栏壁纸
博主头像
王一川博主等级

努力成为一个不会前端的全栈工程师

  • 累计撰写 70 篇文章
  • 累计创建 20 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

ClickHouse

王一川
2021-08-13 / 6 评论 / 1 点赞 / 11,433 阅读 / 14,419 字
温馨提示:
本文最后更新于 2021-10-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一、ClickHouse 概述

ClickHouse 是俄罗斯的 Yandex 于2016年开源的列式存储数据库(DBMS),使用 C++ 编写,主要用于在线分析处理查询(OLAP),能够使用 SQL 查询实时生成分析数据报告。

1.1 ClickHouse 特点

1.1.1 列式存储

对于这样一份数据

IDNAMEAGE
1张三18
2李四19
3王五20

在传统的行式数据库系统中(MySQL、Oracle等),处于同一行的数据总是被物理的存储在一起。即:

行存储

在列式数据库系统中(HBase、Druid等),来自同一列的数据被存储在一起,不同列的数据被单独存储。即:

列存储

当然不同的存储方式适用不同的业务场景,如:行存储针对 select * 操作会有较高的效率,列存储则对查询某个或某几个字段会有较高的效率。在 OLAP 业务场景中通常会有如下关键特征:

  • 绝大多数是读请求
  • 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
  • 已添加到数据库的数据不能修改。
  • 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
  • 宽表,即每个表包含着大量的列
  • 查询相对较少(通常每台服务器每秒查询数百次或更少)
  • 对于简单查询,允许延迟大约50毫秒
  • 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
  • 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
  • 事务不是必须的
  • 对数据一致性要求低
  • 每个查询有一个大表。除了他以外,其他的都很小。
  • 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的 RAM 中

因此针对 OLAP 业务场景,列式存储会有更好的发挥,其处理速度至少提高100倍(官方说的)

行存储

1.1.2 DBMS

ClickHouse 不单单是一个数据库, 它是一个数据库管理系统。因为它允许在运行时创建表和数据库、加载数据和运行查询,而无需重新配置或重启服务。几乎覆盖了标准 SQL 的大部分语法,包括 DDL 和 DML,以及配套的各种函数,用户管理及权限管理,数据的备份与恢复。

1.1.3 数据压缩

除了在磁盘空间和 CPU 消耗之间进行不同权衡的高效通用压缩编解码器之外,ClickHouse 还提供针对特定类型数据的专用编解码器,这使得 ClickHouse 能够与更小的数据库(如时间序列数据库)竞争并超越它们。

1.1.4 磁盘存储

ClickHouse 被设计用于工作在传统磁盘上的系统,它提供每 GB 更低的存储成本,但如果可以使用 SSD 和内存,它也会合理的利用这些资源。

1.1.5 数据分区和线程级并行

ClickHouse 将数据划分为多个 partition,每个 partition 再进一步划分为多个 index granularity(索引粒度),然后通过多个 CPU 核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条 Query 就能利用整机所有 CPU。极致的并行处理能力,极大的降低了查询延时。所以,ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多 cpu,就不利于同时并发多条查询。所以对于高 qps 的查询业务,ClickHouse 并不是强项。

1.1.6 多样化引擎

ClickHouse 和 MySQL 类似,把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。目前包括合并树、日志、接口和其他四大类20多种引擎。

1.1.7 其他特点

  • 实时的数据更新(高效写入)
  • 适合在线查询
  • 支持近似计算
  • 支持数据复制和数据完整性

1.2 ClickHouse 安装

1.2.1 取消打开文件数限制

vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
* soft nproc 131072
* hard nproc 131072

vim /etc/security/limits.d/20-nproc.conf
* soft nofile 65536
* hard nofile 65536
* soft nproc 131072
* hard nproc 131072

重启服务器

1.2.2 安装依赖

yum install -y libtool
yum install -y *unixODBC*

1.2.3 下载安装包

下载地址:https://repo.yandex.ru/clickhouse/tgz/stable/

下载目录(注意下载相同版本的rpm文件)

clickhouse-client-xxx.noarch.rpm
clickhouse-common-static-xxx.x86_64.rpm
clickhouse-common-static-dbg-xxx.x86_64.rpm
clickhouse-server-xxx.noarch.rpm

1.2.4 安装rpm

mkdir clickhouse
## 将安装包移入其中
rpm -ivh *.rpm

1.2.5 修改配置文件

vim /etc/clickhouse-server/config.xml

打开注释 <listen_host>::</listen_host>,允许远程访问,同时防火墙开放端口 8123

配置信息

1.2.6 启动服务

## 启动 ClickHouse
systemctl start clickhouse-server.service
## 重启 ClickHouse
systemctl restart clickhouse-server.service
## 停止 ClickHouse
systemctl stop clickhouse-server.service
## 状态查询
systemctl status clickhouse-server.service
## 开机自启
systemctl enable clickhouse-server.service
## 关闭开机自启
systemctl disable clickhouse-server.service

1.2.7 本地登录

## 安装过程中为 default 用户设置的密码 -m 命令行多行输入
clickhouse-client -m --password=xxx
## 更多帮助信息
clickhouse-client -help

二、数据类型

数据类型对大小写敏感,即 Int 和 INT 不是一回事

2.1 整型

固定长度的整型

2.1.1 有符号

数据类型数据范围
Int8-128 ~ 127
Int16-32768 ~ 32767
Int32-2147483648 ~ 2147483647
Int64-9223372036854775808 ~ 9223372036854775807

2.1.2 无符号

数据类型数据范围
UInt80 ~ 255
UInt160 ~ 65535
UInt320 ~ 4294967295
UInt640 ~ 18446744073709551615

2.2 浮点型

Float32 => java 的 float

Float64 => java 的 double

浮点数计算可能会丢失精度

丢精度

正无穷 Inf

负无穷 -Inf

非数字 NaN

2.3 Decimal

高精度浮点数

数据类型数据范围
Decimal32(n)0<=n<=9,整数位 9-n,小数位 n
Decimal64(n)0<=n<=18,整数位 18-n,小数位 n
Decimal128(n)0<=n<=38,整数位 38-n,小数位 n

小数位超过的部分采取截断,非四舍五入,如:

create table v_t_1(
    a Decimal32(5)
) engine = TinyLog;

insert into v_t_1 values (2/3);

由于现代计算机不支持128位,因此 Decimal128 上的操作由软件模拟,所以 Decimal128 的速度要明显慢于 Decimal32/Decimal64

2.4 字符型

2.4.1 定长

FixedString(N),固定长度 N 的字符串;当数据长度恰好为 N 的时候效率最高

  • 若插入数据包含的字节小于 N,自动在字符串末尾用空字符补齐
  • 若插入数据包含的字节大于 N,抛 Too large value for FixedString(N) 异常

注:做数据查询时,ClickHouse 不会删除末尾空字符,因此在 ClickHouse 中针对 FixedString 类型的字段做 length 返回 N,做 where 查询时会自动删除末尾空字符

2.4.2 不定长

String 存储任意长度的字符串,包括空字节,可代替如 VARCHAR、BLOB、CLOB 等

2.5 布尔值

ClickHouse 没有布尔值,使用 UInt8 类型替代,取值限定为 0 或 1

2.6 时间类型

数据类型数据格式
Date年-月-日
DateTime年-月-日 时:分:秒
DateTime64年-月-日 时:分:秒.亚秒

更多数据类型,可参考官方文档:https://clickhouse.tech/docs/en/sql-reference/data-types/

三、数据库引擎

数据库起到命名空间的作用,可以有效的规避表命名冲突的问题,在 ClickHouse 中任何一张表都必须归属在某个数据库之下,且 ClickHouse 安装中自动创建两个默认数据库:default、system

在 ClickHouse 中数据库也有自己的引擎,目前支持的数据库引擎有5中:

  1. Ordinary:默认引擎,无需可以声明,该数据库引擎下允许创建任意表引擎
  2. Dictionary:字典引擎,会自动为所有数据字典创建他们的数据表
  3. Memory:内存引擎,该数据库下的所有表只会停留在内存中,服务重启会清空数据
  4. Lazy:日志引擎,该数据库下只能使用 Log 系列的表引擎
  5. MySQL:MySQL引擎,自动拉取远端 MySQL 中的数据,并为他们创建 MySQL 表引擎的数据表

3.1 MySQL数据库引擎

建库语法

CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
ENGINE = MySQL('host:port', 'database', 'user', 'password')

同时自动创建MySQL表引擎的数据表

show create api_test

四、表引擎

官方文档: https://clickhouse.tech/docs/en/engines/table-engines

表引擎是 ClickHouse 的一大特色,可以说,表引擎决定了如何存储表的数据,包括:

  1. 数据的存储方式和位置,即写到哪里和从哪里读
  2. 支持哪些查询以及如何支持
  3. 是否支持并发访问
  4. 是否支持多线程
  5. 数据复制
  6. 等等...

表引擎在创建表的时候必须显式的指定,以及该引擎的相关参数

注:引擎名区分大小写

4.1 TinyLog

以列文件的形式存储在磁盘中,不支持索引,没有并发控制。一般用于测试,存储少量数据的小表

CREATE TABLE t_tinylog(
	id Int64,
  name String
)
ENGINE = TinyLog;

4.2 Memory

内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过10G/s)

一般用到它的地方不多,除了用来测试,就是在需要非常高的性能,同时数据量又不太大(上限大概1 亿行)的场景。

CREATE TABLE t_memory(
	id Int64,
  name String
)
ENGINE = Memory;

4.3 MergeTree

ClickHouse 中最强大的表引擎当属 MergeTree(合并树)引擎及该系列(*MergeTree)中的其他引擎,支持索引和分区,地位可以相当于Mysql的innodb。而且基于 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,
    ...
    PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
    PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
    [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name=value, ...]

众多表引擎参数中大多数都是可选,但 order by 是必选的,例如:

CREATE TABLE t_goods
(
    id          UInt32,
    name        String,
    price       Decimal64(5),
    create_time DateTime
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name);

插入数据

insert into t_goods values 
(101, 'good_001', 1000.00, '2020-06-01 12:00:00'),
(102, 'good_002', 2000.00, '2020-06-01 11:00:00'),
(102, 'good_003', 6000.00, '2020-06-02 12:00:00');

4.3.1 primary key

ClickHouse 的主键与其他数据库的主键略有不同,提供了数据的一级索引,但没有唯一约束,这就意味着,在 ClickHouse 中的主键列可以存在相同的数据。其主键的设定主要依据的是查询语句中的 where 条件。

同时,ClickHouse 的主键在存储上采用了稀疏索引的方式,通过对主键的二分查找来定位到数据的大致范围,避免全局扫描,这种设计方式合理的避开了 hive 索引的弊端,也是 hive 在新版本中抛弃索引的原因之一,在 OLAP 的应用场景,给每条数据都添加索引是不显示。那么稀疏索引的粒度(两个索引见的数据间隔)是多少呢?

通过查看建表语句发现,系统默认添加了一个参数项

SETTINGS index_granularity = 8192

该参数的作用就是配置索引粒度,默认 8192,即每隔 8192 条数据创建一个索引,图例如下:

细心的朋友已经发现了,这样做的前提是主键必须是有序的,这也是为什么建表是 order by 是必须的,同时主键的选取也不是随意的,有着严格的要求。

索引粒度的默认值 8192 官方不建议修改,只有当主键存在大量重复数据,比如好几万,这样默认的 8192 创建的索引会有很多重复,此时可以手动的将索引粒度调整为合适的数值,官方文档:https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/mergetree/#mergetree-data-storage

同时主键的选取见 order by

4.3.2 partition by

其作用是分文件夹,避免全表扫描,提高查询效率。使用默认的客户端查询带有分区的表其结果显示如下:

非常人性,且是按照order by的字段进行分区内排序。

若没有指定分区,则默认一个分区,且底层文件夹的命名格式为 分区值_最小分区块编号_最大分区块编号_合并层级 ,其中分区值命名规则如下:

  1. 没定义分区键,文件夹统一命名为 all
  2. 分区键为整型,直接使用该整型的字符串形式
  3. 分区键为日期型,或者可以转换为日期类型的字符串,使用日期格式的字符串形式
  4. 其他类型,如String、浮点型等,通过 128 位的 Hash 值形式

最小分区块编号:自增类型,从 1 开始向上递增,每产生一个分区自增 1

最大分区块编号:字面意思,新建分区是最小值等于最大值

合并层级:分区合并次数

数据磁盘存储的默认位置 /var/lib/clickhouse/data/数据库名称

分区表的目录结构

[root@VM-4-2-centos t_goods]# ll
total 16
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200601_1_1_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200602_2_2_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:32 detached
-rw-r----- 1 clickhouse clickhouse    1 Aug 17 20:32 format_version.txt

可以看到有两个分区,每个分区最小最大分区块编号都一样,且合并次数为0,这就意味着,此时没有发生合并

为什么要发生合并?

若接触过 HBase 的朋友都知道,HBase 存在大合并小合并的概念,使用的是 LSM Tree 的思想,即数据的更新和删除不是真正的操作,比如更新就是插入一条新的数据,只是他的版本号比原始数据大,删除可以打一个墓碑标记,只有在合并的时候过期数据才会被删除,ClickHouse 的合并也是这样

进入其中一个分区

[root@VM-4-2-centos 20200601_1_1_0]# ll
total 36
-rw-r----- 1 clickhouse clickhouse 259 Aug 17 20:35 checksums.txt
-rw-r----- 1 clickhouse clickhouse 109 Aug 17 20:35 columns.txt
-rw-r----- 1 clickhouse clickhouse   1 Aug 17 20:35 count.txt
-rw-r----- 1 clickhouse clickhouse 156 Aug 17 20:35 data.bin
-rw-r----- 1 clickhouse clickhouse 144 Aug 17 20:35 data.mrk3
-rw-r----- 1 clickhouse clickhouse  10 Aug 17 20:35 default_compression_codec.txt
-rw-r----- 1 clickhouse clickhouse   8 Aug 17 20:35 minmax_create_time.idx
-rw-r----- 1 clickhouse clickhouse   4 Aug 17 20:35 partition.dat
-rw-r----- 1 clickhouse clickhouse   8 Aug 17 20:35 primary.idx

bin文件:数据文件
mrk文件:标记文件
标记文件在 idx索引文件 和 bin数据文件 之间起到了桥梁作用。
以mrk2结尾的文件,表示该表启用了自适应索引间隔。
primary.idx文件:主键索引文件,用于加快查询效率。
minmax_create_time.idx:分区键的最大最小值。
checksums.txt:校验文件,用于校验各个文件的正确性。存放各个文件的size以及hash值。
count.txt:记录分区的数据行数,当你执行 count() 聚合时,会在极快的时间给你返回,因为提前算好了(战斗民族的风格)

当我们再次插入数据

insert into t_goods values 
(101, 'good_001', 1000.00, '2020-06-01 12:00:00'),
(102, 'good_002', 2000.00, '2020-06-01 11:00:00'),
(102, 'good_003', 6000.00, '2020-06-02 12:00:00');

理论上此时还是两个分区,查询出来的样式也是分为两块

实际上此时并没有触发合并,任何一批次的数据写入都会进入一个临时的分区,不会纳入任何已存在的分区,写入后的一段时间(10-15分钟),ClickHouse 会自动执行合并操作,把临时分区的数据合并,也可手动执行。

此时数据目录如下

[root@VM-4-2-centos t_goods]# ll
total 24
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200601_1_1_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:36 20200601_3_3_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200602_2_2_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:36 20200602_4_4_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:32 detached
-rw-r----- 1 clickhouse clickhouse    1 Aug 17 20:32 format_version.txt

手动触发合并

optimize table t_goods final;

再次查看数据目录

[root@VM-4-2-centos t_goods]# ll
total 32
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200601_1_1_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:49 20200601_1_3_1
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:36 20200601_3_3_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:35 20200602_2_2_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:49 20200602_2_4_1
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 21:36 20200602_4_4_0
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 17 20:32 detached
-rw-r----- 1 clickhouse clickhouse    1 Aug 17 20:32 format_version.txt

可以看出,分区值为 20200601 的原先是有两个且最大最小分区块编号为1,3,因此合并后的 20200601 的最小分区块编号为 1,最大为 3,且合并层级为 1,一段时间之后原先的两个分区目录将会被删除,此时查询数据也会显示数据被合并到原有的分区中。

4.3.3 order by

order by 是 MegerTree 中唯一一个必填项,甚至比 primary key 还要重要,后面家族的其他引擎(去重、汇总)都是基于 order by 的处理,同时上面说的 primary key 的稀疏索引也是基于此。

主键和order by的关系:主键必须是 order by 的前缀字段,如:order by (a,b,c) 那么主键只能是 (a)、(a,b)、(a,b,c),不能是(b)、(a,c);即:必须按照 order by 的字段顺序选取主键,不能越过其中任意一个字段,其原理也很好理解,当有多个字段排序是,a字段相同按照b字段排序,b字段再相同按照c字段排序,而主键自带的稀疏索引必须要求字段有序。

4.3.4 二级索引

二级索引在 v20.1.2.4 版本前为实现性功能,使用 set allow_experimental_data_skipping_indices=1;打开,之后的版本默认开启,同时剔除该配置项。

二级索引的主要功能是对一级索引的索引,其本身也是一个稀疏索引,要求设置索引粒度(这个时候就不要仿照主键的索引粒度 8192 了),实现了类似跳表的功能。

例子:

CREATE TABLE t_goods
(
    id          UInt32,
    name        String,
    price       Decimal64(5),
    create_time DateTime,
    INDEX idx_price price TYPE minmax GRANULARITY 5
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name);

语法格式为:INDEX 索引名称 索引字段 TYPE 索引类型 GRANULARITY 粒度

其中索引类型:

  • minmax
    存储指定表达式的极值(如果表达式是 tuple ,则存储 tuple 中每个元素的极值),这些信息用于跳过数据块,类似主键。

  • set(max_rows)
    存储指定表达式的不重复值(不超过 max_rows 个,max_rows=0 则表示『无限制』)。这些信息可用于检查数据块是否满足 WHERE 条件。

  • ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
    存储一个包含数据块中所有 n元短语(ngram) 的 布隆过滤器 。只可用在字符串上。
    可用于优化 equalslikein 表达式的性能。

    • n – 短语长度。

    • size_of_bloom_filter_in_bytes – 布隆过滤器大小,字节为单位。(因为压缩得好,可以指定比较大的值,如 256 或 512)。

    • number_of_hash_functions – 布隆过滤器中使用的哈希函数的个数。

    • random_seed – 哈希函数的随机种子。

    • tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
      ngrambf_v1 类似,但是存储的是token而不是ngrams。Token是由非字母数字的符号分割的序列。

    • bloom_filter(bloom_filter([false_positive]) – 为指定的列存储布隆过滤器,可选参数false_positive用来指定从布隆过滤器收到错误响应的几率。取值范围是 (0,1),默认值:0.025支持的数据类型:Int*, UInt*, Float*, Enum, Date, DateTime, String, FixedString, Array, LowCardinality, Nullable

4.3.5 TTL

MegerTree 提供了可以管理数据表或数据列生命周期的功能,即:TTL(Time To Live)

1.列级别

创建表,设置 price 的过期时间为 create_time 的一分钟后

CREATE TABLE t_goods_ttl
(
    id          UInt32,
    name        String,
    price       Decimal64(5) TTL create_time + interval 1 minute,
    create_time DateTime
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name);

插入数据,create_time 已实际时间为准

insert into t_goods_ttl
values (101, 'good_001', 1000.00, '2021-08-18 11:04:00'),
       (102, 'good_002', 2000.00, '2021-08-17 11:04:00'),
       (102, 'good_002', 6000.00, '2021-08-19 11:04:00');

查询数据(一分钟已过)

这是因为只有在合并期间才会清理数据,因此可以手动触发合并

optimize table t_goods_ttl final;

被清除的过期列会被重置成字段类型的默认值

过期的列默认被删除,但也可以指定将数据片段移至磁盘、合并等操作。官方文档:https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/mergetree/

2.表级别

创建表,设置表的过期时间为 create_time 的一分钟后

CREATE TABLE t_goods_ttl2
(
    id          UInt32,
    name        String,
    price       Decimal64(5),
    create_time DateTime
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name)
TTL create_time + interval 1 minute;

插入数据

insert into t_goods_ttl2
values (101, 'good_001', 1000.00, '2021-08-18 11:18:00'),
       (102, 'good_002', 2000.00, '2021-08-17 11:18:00'),
       (102, 'good_002', 6000.00, '2021-08-19 11:18:00');

插入瞬间 2021-08-17 11:18:00 的数据就被清理了,手动触发合并后

注:手动触发合并,ClickHouse 会另起一个线程进行后台合并,且合并期间会消耗大量资源;因此手动触发合并后,不一定立刻就可以看到数据被清理,包括列级别、表级别的 TTL
补:过期时间单位可选「second、minute、hour、day、week、month、quarter、year」

4.4 ReplacingMergeTree

ReplacingMergeTree 是 MergeTre e的一个变种,它存储特性完全继承 MergeTree,只是多了一个去重的功能。尽管 MergeTree 可以设置主键,但是 primary key 没有唯一约束的功能。如你想处理掉重复的数据,可以借助这个 ReplacingMergeTree。

4.4.1 去重时机

数据的去重只会在合并的过程中出现。合并会在未知的时间在后台进行,所以你无法预先作出计划。有一些数据可能仍未被处理。因此 ReplacingMergeTree 只能保证数据的最终一致性,后续会介绍如何保证数据的一致性。

4.4.2 去重范围

分区内去重,无法进行跨分区去重;因此 ReplacingMergeTree 适用于后台清除重复数据以节省空间,但不能保证没有重复的数据出现

4.4.3 实例操作

创建测试表

CREATE TABLE t_goods_replace
(
    id          UInt32,
    name        String,
    price       Decimal64(5),
    create_time DateTime
) ENGINE = ReplacingMergeTree(create_time)
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name);

ReplacingMergeTree([column]),填入参数为版本字段,当 order by 字段一致时,保留版本字段最大的那条数据,若不指定则按照插入顺序,保留最新数据

插入数据,新版本在插入数据的同时对同一批次进行去重

insert into t_goods_replace
values (101, 'sku_001', 1000.00, '2020-06-01 12:00:00'),
       (102, 'sku_002', 2000.00, '2020-06-01 11:00:00'),
       (102, 'sku_004', 2500.00, '2020-06-01 12:00:00'),
       (102, 'sku_002', 2000.00, '2020-06-01 13:00:00'),
       (102, 'sku_002', 12000.00, '2020-06-01 13:00:00'),
       (102, 'sku_002', 600.00, '2020-06-02 12:00:00');

查询数据,相同 id、name 的数据保留 create_time 最大的那条。

再次插入相同数据

此时就会出现重复数据,需要等待合并实际才会进行去重,手动触发去重

总结:
➢ 实际上是使用order by 字段作为唯一键
➢ 去重不能跨分区
➢ 只有同一批插入(新版本)或合并分区时才会进行去重
➢ 认定重复的数据保留,版本字段值最大的
➢ 如果版本字段相同则按插入顺序保留最后一笔

4.5 SummingMergerTree

对于不查询明细,只关心以维度进行汇总聚合结果的场景。如果只使用普通的 MergeTree 的话,无论是存储空间的开销,还是查询时临时聚合的开销都比较大。ClickHouse 为了这种场景,提供了一种能够分区内预聚合的引擎 SummingMergeTree

创建测试表

CREATE TABLE t_goods_sum
(
    id          UInt32,
    name        String,
    price       Decimal64(5),
    create_time DateTime
) ENGINE = SummingMergeTree(price)
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, name);

注:类似 ReplacingMergeTree 表引擎,参数列可以省略不写,若 SummingMergeTree 的参数列不写,所有非纬度列(非 order by 指定的字段)且为数字的的字段都会进行汇总,通常会指定聚合列。

插入数据,同一批次的数据会在插入时根据纬度列进行一次分区内聚合

insert into t_goods_sum
values (101, 'sku_001', 1000.00, '2020-06-01 12:00:00'),
       (102, 'sku_002', 2000.00, '2020-06-01 11:00:00'),
       (102, 'sku_004', 2500.00, '2020-06-01 12:00:00'),
       (102, 'sku_002', 2000.00, '2020-06-01 13:00:00'),
       (102, 'sku_002', 12000.00, '2020-06-01 13:00:00'),
       (102, 'sku_002', 600.00, '2020-06-02 12:00:00');

同理再次插入数据,不会与原先数据进行合并,等待合并时机才会进行聚合操作,可以手动触发合并观察效果

注:optimize table xxx final;慎用!!!仅做测试方便快速查看结果,合并过程中会影响表的读写操作

因此,SummingMergeTree 对于聚合操作也是保证数据的最终一致性,当我们要查询聚合结果时,仍然使用 select sum(x) from xxx 而不是 select * from xxx因为可能存在还未聚合的数据。

总结:
➢ 以 SummingMergeTree() 中指定的列作为汇总数据列
➢ 可以填写多列必须数字列,如果不填,以所有非维度列且为数字列的字段为汇总数据列
➢ 以 order by 的列为准,作为维度列
➢ 其他的列按插入顺序保留第一行
➢ 不在一个分区的数据不会被聚合
➢ 只有在同一批次插入(新版本)或分片合并时才会进行聚合

今天先到这...饿了

1

评论区