Clickhouse冷热数据分离实践
配置多卷存储策略
使用Clickhouse的存储策略功能,可以实现冷热数据分离存储。
我们可以将业务上访问频繁的数据放到热存储区(如高性能SSD磁盘),将业务上较少访问的数据放在冷存储区(如价格更便宜、空间更大的磁盘上,Clickhouse还支持挂着S3协议的存储)。
本文将详细介绍具体的配置方法:
1、配置磁盘
2、配置存储策略
3、指定表的存储策略
1、配置磁盘
配置默认磁盘
默认磁盘路径在标签<clickhouse><path></path></clickhouse>下配置:
<clickhouse> <path>/data/clickhouse/clickhouse/</path> </clickhouse>
这个配置可以放在主配置文件config.xml中,也可以放在config.d目录下的某个文件中。
在clickhouse的系统表system.disks中,可以看到当前实例默认磁盘的存储路径,以及空间使用情况。
ck01 :) select * from system.disks; SELECT * FROM system.disks Query id: d27a898b-6d6f-4322-b491-f80f5eeee922 ┌─name────┬─path─────────────────────────┬──free_space─┬──total_space─┬─keep_free_space─┬─type──┬─cache_path─┐ │ default │ /data/clickhouse/clickhouse/ │ 66161168384 │ 103240073216 │ 0 │ local │ │ └─────────┴──────────────────────────────┴─────────────┴──────────────┴─────────────────┴───────┴────────────┘ 1 row in set. Elapsed: 0.036 sec.
配置额外的磁盘
额外的磁盘需要在<storage_configuration>标签下配置:
<clickhouse> <storage_configuration> <disks> <default> </default> <disk2> <path>/data2/</path> </disk2> </disks> </storage_configuration> </clickhouse>
disks配置有几点需要注意
路径要以/结尾
磁盘的名称和路径不能重复,也不能和默认磁盘的路径重复。否则会导致clickhouse启动失败
2、配置存储策略
需要配置存储策略,才能使用新添加的磁盘。
我们可以在系统表system.storage_policies表里查到当前的策略:
ck01 :) select * from system.storage_policies; SELECT * FROM system.storage_policies Query id: 7674ae4d-b4b2-49fe-98be-117c39e7f519 ┌─policy_name─┬─volume_name─┬─volume_priority─┬─disks───────┬─volume_type─┬─max_data_part_size─┬─move_factor─┬─prefer_not_to_merge─┐ │ default │ default │ 1 │ ['default'] │ JBOD │ 0 │ 0 │ 0 │ └─────────────┴─────────────┴─────────────────┴─────────────┴─────────────┴────────────────────┴─────────────┴─────────────────────┘
默认情况下有一条default策略:所有的数据都写入到default磁盘中。
配置存储策略:
<clickhouse> <storage_configuration> <disks> <default> </default> <disk2> <path>/data2/</path> </disk2> </disks> <policies> <from_default_to_disk2> <volumes> <vol_default> <disk>default</disk> <max_data_part_size_bytes>1073741824</max_data_part_size_bytes> <load_balancing>round_robin</load_balancing> </vol_default> <vol_disk2> <disk>disk2</disk> </vol_disk2> </volumes> <move_factor>0.2</move_factor> </from_default_to_disk2> </policies> </storage_configuration> </clickhouse>
配置后,可以到配置表中确认配置信息
ck01 :) select * from system.storage_policies; SELECT * FROM system.storage_policies Query id: d3143ebb-14f9-4836-be16-7a5ab11a52a5 ┌─policy_name───────────┬─volume_name─┬─volume_priority─┬─disks───────┬─volume_type─┬─max_data_part_size─┬─move_factor─┬─prefer_not_to_merge─┐ │ default │ default │ 1 │ ['default'] │ JBOD │ 0 │ 0 │ 0 │ │ from_default_to_disk2 │ vol_default │ 1 │ ['default'] │ JBOD │ 1073741824 │ 0.2 │ 0 │ │ from_default_to_disk2 │ vol_disk2 │ 2 │ ['disk2'] │ JBOD │ 0 │ 0.2 │ 0 │ └───────────────────────┴─────────────┴─────────────────┴─────────────┴─────────────┴────────────────────┴─────────────┴─────────────────────┘
在上面的例子中,我们配置了一个存储策略:
1、包含了2个卷(volume)。数据默认写入到default磁盘。
2、move factor设置为0.2。当default磁盘空间小于20%时,将数据迁移到磁盘data2。
3、默认卷max_data_part_size_bytes设置为1G,大于1G的part数据,不会写入到默认卷。
写入数据时,会根据策略下定义的卷的顺序,来确定数据写入到哪个卷。
3、添加表的存储策略
创建表的时候,如果不指定存储策略,则会使用default存储策略。
我们可以在系统表system.tables中查看表的存储策略:
ck01 :) select database, table, engine, storage_policy from system.tables where table='metrics' and database='local'; SELECT database, table, engine, storage_policy FROM system.tables WHERE (table = 'metrics') AND (database = 'local') Query id: 7a7fe08f-8297-40d4-952c-ad454c50e56e ┌─database─┬─table───┬─engine────┬─storage_policy─┐ │ local │ metrics │ MergeTree │ default │ └──────────┴─────────┴───────────┴────────────────┘ 1 row in set. Elapsed: 0.002 sec.
可以在简表时指定表的存储策略,也可以修改已有的表的存储策略。
修改表的存储策略:
修改存储策略时,新的存储策略需要包含老的存储策略的所有数据卷,否则会报错:
ck01 :) alter table local.metrics modify setting storage_policy='from_default_to_disk2'; ALTER TABLE local.metrics MODIFY SETTING storage_policy = 'from_default_to_disk2' Query id: a0e034c6-8955-4cf9-b47a-399611a35523 0 rows in set. Elapsed: 0.002 sec. Received exception from server (version 22.6.3): Code: 36. DB::Exception: Received from localhost:9000. DB::Exception: New storage policy `default` shall contain volumes of old one. (BAD_ARGUMENTS)
我们需要修改一下存储策略的定义:
<policies> <from_default_to_disk2> <volumes> <default> <disk>default</disk> <max_data_part_size_bytes>1073741824</max_data_part_size_bytes> <load_balancing>round_robin</load_balancing> </default> <vol_disk2> <disk>disk2</disk> </vol_disk2> </volumes> <move_factor>0.2</move_factor> </from_default_to_disk2> </policies>
再次修改存储策略时,就可以成功了:
ck01 :) alter table local.metrics modify setting storage_policy='from_default_to_disk2'; ALTER TABLE local.metrics MODIFY SETTING storage_policy = 'from_default_to_disk2' Query id: a0e034c6-8955-4cf9-b47a-399611a35523 0 rows in set. Elapsed: 0.002 sec. ck01 :) select database, table, engine, storage_policy from system.tables where table='metrics' and database='local'; SELECT database, table, engine, storage_policy FROM system.tables WHERE (table = 'metrics') AND (database = 'local') Query id: 18b7ddf6-cce8-4c51-b1ee-d04619ecb1f1 ┌─database─┬─table───┬─engine────┬─storage_policy────────┐ │ local │ metrics │ MergeTree │ from_default_to_disk2 │ └──────────┴─────────┴───────────┴───────────────────────┘ 1 row in set. Elapsed: 0.002 sec.
移动表的数据
有几种方式可以将表中的数据移动到制定存储卷中:
1、基于存储策略的move_factor,系统自动移动数据。
2、使用命令手动将数据移动到指定存储卷。
3、建表时指定TTL,将超过TTL时间的数据存储到指定的存储卷。
使用命令移动数据
可以使用alter table move partition命令将指定分区移动到指定的卷或磁盘。当然,表的当前存储策略必须存在指定的卷或磁盘。
ck01 :) select partition, name from system.parts where table = 'metrics'; SELECT partition, name FROM system.parts WHERE table = 'metrics' Query id: 26f55d64-6ff5-4dc5-9ba0-ec5340693c0c ┌─partition─┬─name─────────────┐ │ 20221130 │ 20221130_16_19_1 │ └───────────┴──────────────────┘ 1 row in set. Elapsed: 0.002 sec. ck01 :) alter table metrics move partition 20221130 to volume 'vol_disk2'; ALTER TABLE metrics MOVE PARTITION 20221130 TO VOLUME 'vol_disk2' Query id: 5ee042d5-a2e0-44d4-b98f-8db0d8265355 Ok.
移动后,可以看到相关数据文件也进行了移动:
ck01 :) select partition, name, disk_name, path from system.parts where table = 'metrics'; SELECT partition, name, disk_name, path FROM system.parts WHERE table = 'metrics' Query id: df4c3140-4ece-457d-9364-249719ba5105 ┌─partition─┬─name─────────────┬─disk_name─┬─path────────────────────────────────────────────────────────────────────┐ │ 20221130 │ 20221130_16_19_1 │ disk2 │ /data2/store/def/def88518-fd7b-418d-a7dd-6564e38bba39/20221130_16_19_1/ │ └───────────┴──────────────────┴───────────┴─────────────────────────────────────────────────────────────────────────┘
将分区移动到指定磁盘:
ck01 :) alter table metrics move partition 20221130 to disk 'default'; ALTER TABLE metrics MOVE PARTITION 20221130 TO DISK 'default' Query id: 0e71441c-9182-46f2-9393-84acb1ed6ed7 Ok. 0 rows in set. Elapsed: 0.002 sec. ck01 :) select partition, name, disk_name, path from system.parts where table = 'metrics'; SELECT partition, name, disk_name, path FROM system.parts WHERE table = 'metrics' Query id: 304271c2-0b06-4689-8c77-340fde6f3585 ┌─partition─┬─name─────────────┬─disk_name─┬─path─────────────────────────────────────────────────────────────────────────────────────────┐ │ 20221130 │ 20221130_16_19_1 │ default │ /data/clickhouse/clickhouse/store/def/def88518-fd7b-418d-a7dd-6564e38bba39/20221130_16_19_1/ │ └───────────┴──────────────────┴───────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
根据TTL指定存储卷
CREATE TABLE example_table ( d DateTime, a Int ) ENGINE = MergeTree PARTITION BY toYYYYMMDD(d) ORDER BY d TTL d + INTERVAL 1 MONTH DELETE, d + INTERVAL 1 WEEK TO VOLUME 'default', d + INTERVAL 2 WEEK TO DISK 'disk2' settings storage_policy='from_default_to_disk2'
注意,TTL里指定的disk, volume必须是指定storage_policy里存在的。
写入一些数据:
insert into example_table values(now(), 1); insert into example_table values(now() - interval 5 day, 2); insert into example_table values(now() - interval 10 day, 3); insert into example_table values(now() - interval 15 day, 4); insert into example_table values(now() - interval 20 day, 5); insert into example_table values(now() - interval 25 day, 6); insert into example_table values(now() - interval 30 day, 7); insert into example_table values(now() - interval 35 day, 8);
查看数据存储在哪个磁盘:
ck01 :) select partition, name,active, disk_name from system.parts where table = 'example_table' order by partition; SELECT partition, name, active, disk_name FROM system.parts WHERE table = 'example_table' ORDER BY partition ASC Query id: cf10d901-c41b-4094-9761-02f0a8ac916e ┌─partition─┬─name───────────┬─active─┬─disk_name─┐ │ 20221114 │ 20221114_8_8_0 │ 0 │ disk2 │ │ 20221119 │ 20221119_7_7_0 │ 0 │ disk2 │ │ 20221124 │ 20221124_6_6_0 │ 1 │ disk2 │ │ 20221129 │ 20221129_5_5_0 │ 1 │ disk2 │ │ 20221204 │ 20221204_4_4_0 │ 1 │ disk2 │ │ 20221209 │ 20221209_3_3_0 │ 1 │ default │ │ 20221214 │ 20221214_2_2_0 │ 1 │ default │ │ 20221219 │ 20221219_1_1_0 │ 1 │ default │ └───────────┴────────────────┴────────┴───────────┘
可以看到,离当前时间(2022-12-19)近(1周内)的数据,存在在default磁盘。其他数据存储在disk2磁盘。
过期的分区会自动被删除。