前文如下:
11.【clickhouse】ClickHouse从入门到放弃-概述
12.【clickhouse】ClickHouse从入门到放弃-环境搭建
13.【clickhouse】ClickHouse从入门到放弃-引擎
14.【clickhouse】ClickHouse从入门到放弃-实战
55.【clickhouse】ClickHouse从入门到放弃-概念场景
56.【clickhouse】ClickHouse从入门到放弃-架构概述
57.【clickhouse】ClickHouse从入门到放弃-update和delete的使用
58.【clickhouse】ClickHouse从入门到放弃-数据类型转换
文档参考:《ClickHouse原理解析与应用实践(数据库技术丛书)(朱凯)》
1.分区表
数据分区(partition)和数据分片(shard)是完全不同的两个概念。数据分区是针对本地数据而言的,是数据的一种纵向切分。而数据分片是数据的一种横向切分(后面会详细介绍)。数据分区对于一款OLAP数据库而言意义非凡:借助数据分区,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作。 假设数据表按照月份分区,那么数据就可以按月份的粒度被替换更新。
分区虽好,但不是所有的表引擎都可以使用这项特性,目前只有合并树(MergeTree)家族系列的表引擎才支持数据分区。接下来通过一个简单的例子演示分区表的使用方法。首先由PARTITION BY指定分区键,例如下面的数据表partition_v1使用了日期字段作为分区键,并将其格式化为年月的形式。
CREATE TABLE partition_v1 ( ID String, URL String, EventTime Date ) ENGINE = MergeTree() PARTITION BY toYYYYMM(EventTime) ORDER BY ID
接着写入不同月份的测试数据:
INSERT INTO partition_v1 VALUES ('A000','www.nauu.com', '2019-05-01'), ('A001','www.brunce.com', '2019-06-02')
最后通过system.parts系统表,查询数据表的分区状态:
SELECT table,partition,path from system.parts WHERE table = 'partition_v1' ┌─table─────┬─partition─┬─path─────────────────────────┐ │ partition_v1 │ 201905 │ /chbase/data/default/partition_v1/201905_1_1_0/│ │ partition_v1 │ 201906 │ /chbase/data/default/partition_v1/201906_2_2_0/│ └─────────┴────────┴─────────────────────────────┘
可以看到,partition_v1按年月划分后,目前拥有两个数据分区,且每个分区都对应一个独立的文件目录,用于保存各自部分的数据。 合理设计分区键非常重要,通常会按照数据表的查询场景进行针对性设计。例如在刚才的示例中数据表按年月分区,如果后续的查询按照分区键过滤,例如:
SELECT * FROM partition_v1 WHERE EventTime ='2019-05-01'
那么在后续的查询过程中,可以利用分区索引跳过6月份的分区目录,只加载5月份的数据,从而带来查询的性能提升。当然,使用不合理的分区键也会适得其反,分区键不应该使用粒度过细的数据字段。例如,按照小时分区,将会带来分区数量的急剧增长,从而导致性能下降.
2.数据分区的基本操作
2.1 查询分区信息
ClickHouse内置了许多system系统表,用于查询自身的状态信息。其中parts系统表专门用于查询数据表的分区信息。例如执行下面的语句,就能够得到数据表partition_v2的分区状况:
SELECT partition_id,name,table,database FROM system.parts WHERE table = 'partition_v2' ┌─partition_id─┬─name───────┬─table─────┬─database┐ │ 201905 │ 201905_1_1_0_6 │ partition_v2 │ default │ │ 201910 │ 201910_3_3_0_6 │ partition_v2 │ default │ │ 201911 │ 201911_4_4_0_6 │ partition_v2 │ default │ │ 201912 │ 201912_5_5_0_6 │ partition_v2 │ default │ └──────────┴──────────┴─────────┴──────┘
如上所示,目前partition_v2共拥有4个分区,其中partition_id或者name等同于分区的主键,可以基于它们的取值确定一个具体的分区。
2.2 删除指定分区
合理地设计分区键并利用分区的删除功能,就能够达到数据更新的目的。删除一个指定分区的语法如下所示:
ALTER TABLE tb_name DROP PARTITION partition_expr
假如现在需要更新partition_v2数据表整个7月份的数据,则可以先将7月份的分区删除:
ALTER TABLE partition_v2 DROP PARTITION 201907
然后将整个7月份的新数据重新写入,就可以达到更新的目的:
INSERT INTO partition_v2 VALUES ('A004-update','www.bruce.com', '2019-07-02'),…
查验数据表,可以看到7月份的数据已然更新:
SELECT * from partition_v2 ORDER BY EventTime ┌─ID───────┬─URL──────┬ EventTime ┐ │ A001 │ www.nauu.com │ 2019-05-02 │ │ A002 │ www.nauu1.com │ 2019-06-02 │ │ A004-update │ www.bruce.com │ 2019-07-02 │ └─────────┴─────────┴───────┘
2.3 复制分区数据
ClickHouse支持将A表的分区数据复制到B表,这项特性可以用于快速数据写入、多表间数据同步和备份等场景,它的完整语法如下:
ALTER TABLE B REPLACE PARTITION partition_expr FROM A
不过需要注意的是,并不是任意数据表之间都能够相互复制,它们还需要满足两个前提条件:
·两张表需要拥有相同的分区键;
·它们的表结构完全相同。
假设数据表partition_v2与先前的partition_v1分区键和表结构完全相同,那么应先在partition_v1中写入一批8月份的新数据:
INSERT INTO partition_v1 VALUES ('A006-v1','www.v1.com', '2019-08-05'),('A007-v1','www.v1.com', '2019-08-20')
再执行下面的语句:
ALTER TABLE partition_v2 REPLACE PARTITION 201908 FROM partition_v1
即能够将partition_v1的整个201908分区中的数据复制到partition_v2:
SELECT * from partition_v2 ORDER BY EventTime ┌─ID───────┬─URL──────┬─EventTime─┐ │ A000 │ www.nauu.com │ 2019-05-01 │ │ A001 │ www.nauu.com │ 2019-05-02 │ 省略… │ A004-update │ www.bruce.com │ 2019-07-02 │ │ A006-v1 │ www.v1.com │ 2019-08-05 │ │ A007-v1 │ www.v1.com │ 2019-08-20 │ └─────────┴─────────┴───────┘
2.4 重置分区数据
如果数据表某一列的数据有误,需要将其重置为初始值,此时可以使用下面的语句实现:
ALTER TABLE tb_name CLEAR COLUMN column_name IN PARTITION partition_expr
对于默认值的含义,笔者遵循如下原则:如果声明了默认值表达式,则以表达式为准;否则以相应数据类型的默认值为准。例如,执行下面的语句会重置partition_v2表内201908分区的URL数据重置。
ALTER TABLE partition_v2 CLEAR COLUMN URL in PARTITION 201908
查验数据后会发现,URL字段已成功被全部重置为空字符串了(String类型的默认值)。
SELECT * from partition_v2 ┌─ID────┬─URL─┬──EventTime┐ │ A006-v1 │ │ 2019-08-05 │ │ A007-v1 │ │ 2019-08-20 │ └──────┴────┴────────┘
2.5 卸载与装载分区
表分区可以通过DETACH语句卸载,分区被卸载后,它的物理数据并没有删除,而是被转移到了当前数据表目录的detached子目录下。而装载分区则是反向操作,它能够将detached子目录下的某个分区重新装载回去。卸载与装载这一对伴生的操作,常用于分区数据的迁移和备份场景。卸载某个分区的语法如下所示:
ALTER TABLE tb_name DETACH PARTITION partition_expr
例如,执行下面的语句能够将partition_v2表内整个8月份的分区卸载:
ALTER TABLE partition_v2 DETACH PARTITION 201908
此时再次查询这张表,会发现其中2019年8月份的数据已经没有了。而进入partition_v2的磁盘目录,则可以看到被卸载的分区目录已经被移动到了detached目录中:
/chbase/data/data/default/partition_v2/detached # ll total 4 drwxr-x---. 2 clickhouse clickhouse 4096 Aug 31 23:16 201908_4_4_0
记住,一旦分区被移动到了detached子目录,就代表它已经脱离了ClickHouse的管理,ClickHouse并不会主动清理这些文件。这些分区文件会一直存在,除非我们主动删除或者使用ATTACH语句重新装载它们。装载某个分区的完整语法如下所示:
ALTER TABLE tb_name ATTACH PARTITION partition_expr
再次执行下面的语句,就可以将刚才已被卸载的201908分区重新装载回去:
ALTER TABLE partition_v2 ATTACH PARTITION 201908