clickhouse-clickhouse将冷数据存储至OSS
Clickhouse可以设置每个表的存储策略polices,存储策略中定义了卷volumes,每个卷volumes又可以使用定义好的磁盘disk。然后利用表数据的TTL设置功能,将达到TTL条件的数据存储迁移至存储策略下的某个卷中。
配置冷数据写入OSS#
修改配置文件,定义存储策略,卷和磁盘#
编辑 /etc/clickhouse-server/config.xml 或 /etc/clickhouse-server/config.d/oss_storage.xml,在 <clickhouse> 标签内添加 <storage_configuration>标签。
OSS 使用 S3 兼容接口,ClickHouse 从 21.8 版本开始对 s3 磁盘支持就比较稳定了,建议使用 22.x 及以上版本。
CLickhouse中存储有Volume和Disk的概念。Volume关联实际的Disk,然后配置表格将数据写到Volume中。
<clickhouse>
<storage_configuration>
<disks>
<!-- 原有的本地磁盘(默认存在,可不用显式声明) -->
<default>
<path>/var/lib/clickhouse/</path>
</default>
<!-- 新增:OSS 冷数据磁盘 -->
<oss_cold>
<type>s3</type>
<endpoint>https://oss-cn-shanghai.aliyuncs.com</endpoint>
<access_key_id>你的AK</access_key_id>
<secret_access_key>你的SK</secret_access_key>
<region>oss-cn-shanghai</region>
<!-- 建议为每个集群节点设置不同的根路径,避免冲突 -->
<metadata_path>/var/lib/clickhouse/disks/oss_cold/</metadata_path>
</oss_cold>
</disks>
<policies>
<!-- 冷热分层策略 -->
<hot_cold_oss>
<volumes>
<!-- 热数据卷:本地 SSD -->
<hot>
<disk>default</disk>
</hot>
<!-- 冷数据卷:OSS -->
<cold>
<disk>oss_cold</disk>
</cold>
</volumes>
</hot_cold_oss>
</policies>
</storage_configuration>
</clickhouse>
说明:
endpoint填写 OSS 的区域域名(内网域名可免流量费,但需保证网络相通)。metadata_path用于存放本地元数据,必须为每个s3磁盘指定一个唯一的本地路径。- 如果使用 ECS 的 RAM 角色,可设置
<use_environment_credentials>true</use_environment_credentials>并去掉 AK/SK。 - 保存后重启 ClickHouse 使配置生效。
设置表属性并关联存储策略#
新建表并开启冷热分层#
建表时通过 SETTINGS storage_policy = 'hot_cold_oss' 使用刚定义的策略,并用 TTL 指定冷数据自动迁移的时间。
CREATE TABLE test.oss_cold_table
(
`event_time` DateTime,
`user_id` UInt32,
`event` String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_time) -- 按月分区
ORDER BY (user_id, event_time)
TTL event_time + INTERVAL 30 DAY TO VOLUME 'cold' -- 30天前的数据自动转到 cold 卷(即 OSS)
SETTINGS storage_policy = 'hot_cold_oss';
TO VOLUME 'cold'表示数据移动到策略里定义的cold卷(我们配置的是 OSS 盘)。- 也可以直接
TO DISK 'oss_cold',但推荐用卷(volume)以保持灵活性。 - TTL 基于分区键的最小值/最大值触发移动,因此分区粒度要合理(例如按月分区,按月移动)。
如果表已经存在,可以用
ALTER追加 TTL:
ALTER TABLE test.oss_cold_table MODIFY TTL event_time + INTERVAL 30 DAY TO VOLUME 'cold';
已有表开启冷热分层#
1. 确认 ClickHouse 版本(需 ≥ 22.3)#
SELECT version();
需要支持 ALTER TABLE ... MODIFY SETTING storage_policy,否则只能重建表。
2. 将表关联到已配置的存储策略#
- 关联存储策略
ALTER TABLE logs.alert_event_details_xjjc MODIFY SETTING storage_policy = 'hot_cold_oss';
这步让表知道自己有冷热两个卷可用。
2. 修改 TTL,增加 TO VOLUME 规则
ALTER TABLE logs.alert_event_details_xjjc
MODIFY TTL
toDateTime(insert_time) + toIntervalDay(30) TO VOLUME 'cold',
toDateTime(insert_time) + toIntervalDay(180) DELETE;
TTL 会按顺序执行:先移动,后删除。
必须先关联存储策略,再增加TO VOLUME规则。这是因为CLickhouse中Volume(卷)不是全局独立的,它必须“居住”在一个 Storage Policy(存储策略)里。表只有通过绑定策略,才能“看见”并“使用”其中的卷。当表执行 TTL ... TO VOLUME 'cold' 时,数据库会先查看这张表绑定的策略是什么,然后在该策略内查找名为 cold 的卷。
注意事项#
- S3 磁盘上的数据是只读的:ClickHouse 不会在 OSS 上原地更新或合并数据。当本地热盘上的分区需要与冷盘分区合并时,会把 OSS 上的数据暂时下载到本地,合并后再上传回去,可能会产生额外的请求费用和延迟。
- 不要频繁修改 OSS 分区:尽量设计好分区键,让冷数据一次转入后很少变动。
- 副本表需统一配置:如果使用
ReplicatedMergeTree,需要所有副本的config.xml都包含相同的 OSS 磁盘和策略定义,否则复制的MOVE PARTITION操作会失败。 - 网络与权限:确保 ClickHouse 节点能访问 OSS 端点,并且 AK/SK 有读写目标 Bucket 的权限。
- 性能考虑:查询大量位于 OSS 上的数据会比本地磁盘慢(网络延迟),适合不常访问的归档场景。 通过以上配置,你的 ClickHouse 冷数据就可以按时间规则自动、透明地沉降到阿里云 OSS 中,既节省本地存储成本,又不影响查询能力。
验证冷数据自动迁移#
插入一些模拟数据,手动调一下时间,再触发分区合并,观察数据分布。
-- 插入一些“旧”数据,距今超过30天
INSERT INTO test.oss_cold_table
SELECT now() - INTERVAL 60 DAY, number, 'old_event'
FROM numbers(1000);
-- 强制触发分区合并与 TTL 移动(也可等后台自动执行) OPTIMIZE TABLE test.oss_cold_table FINAL; -- 查看每个分区所在的磁盘 SELECT
partition,
name,
disk_name,
active
FROM system.parts
WHERE table = 'oss_cold_table' AND database = 'test'
ORDER BY partition;
你应该能看到,30天以前的分区对应的 disk_name 会变成 oss_cold,而近期的数据仍在 default 磁盘上。
检查表上的存储策略#
查看集群名称#
SELECT
cluster,
shard_num,
shard_weight,
replica_num,
host_name,
port,
is_local
FROM system.clusters
ORDER BY cluster, shard_num, replica_num;
查看存储策略与卷的层次结构#
SELECT
policy_name,
volume_name,
volume_priority,
disk_name,
max_data_part_size, -- 卷级别可选的参数
move_factor
FROM system.storage_policies
ORDER BY policy_name, volume_priority, disk_name;
system.storage_policies 表在较新版本(24.x / 25.x)中已经去掉了 disk_name 列,改用 disks 数组。
SELECT
policy_name,
volume_name,
volume_priority,
disk_array AS disk_name,
max_data_part_size,
move_factor
FROM system.storage_policies
ARRAY JOIN disks AS disk_array
ORDER BY policy_name, volume_priority, disk_name;
查看磁盘#
SELECT
name,
path,
type, -- 磁盘类型(local、s3、cache,objectstorage等a)
free_space,
total_space
FROM system.disks;
产看表的存储策略#
检查log数据库中的alert_event_details_xjjc表应用的存储策略。
SELECT
database,
table,
storage_policy,
create_table_query
FROM system.tables
WHERE database = 'logs' AND table = 'alert_event_details_xjjc';
从OSS中查询数据#
CLickhouse会根据查询的数据自动从OSS中拉取数据并解析,无需手动加载。对用户来说,感觉和查本地数据一样,无需任何特殊语法。
1. 查询时,ClickHouse 自动从 OSS 读数据#
你对表执行一条 SQL:
SELECT user_id, event_time, event
FROM test.oss_cold_table
WHERE event_time < '2025-01-01';
ClickHouse 内部流程:
- 解析 SQL,确定需要扫描哪些分区/part。
- 通过
system.parts发现部分 part 所在磁盘为oss_cold。 - 如果本地缓存中没有,自动从 OSS 下载该 part 的二进制数据(如
marks文件、columns数据块)到本地临时存储(通常是disk的cache或/tmp)。 - 在本地内存/临时文件中完成查询计算,返回结果。
- 临时数据随后可能会被清理,也可配置缓存来保留。
2. 如何确认数据真的在 OSS 上且可查#
用 system.parts 查看分区对应的磁盘:
SELECT
partition,
name,
disk_name,
active,
rows
FROM system.parts
WHERE table = 'oss_cold_table' AND database = 'test'
ORDER BY partition DESC;
你会看到类似结果:
┌─partition─┬─name───────┬─disk_name─┬─active─┬─rows─┐
│ 202507 │ 202507_1_1 │ oss_cold │ 1 │ 500 │
│ 202508 │ 202508_2_2 │ default │ 1 │ 300 │
└───────────┴────────────┴───────────┴────────┴──────┘
disk_name = 'oss_cold'的分区就是已经搬迁到 OSS 上的数据。- 直接查询这些分区,依然可以正常返回结果。
3. 性能优化:减少每次查 OSS 的开销#
因为 OSS 有网络延迟,如果频繁查询同一份冷数据,可配置本地 SSD 缓存,把热访问的 OSS 数据块缓存在本地。
在 storage_configuration 里定义一块 cache 磁盘,叠加在 s3 磁盘上:
<disks>
<oss_cold>
<type>s3</type>
<!-- ... 你的 OSS 配置 ... -->
</oss_cold>
<!-- 给 OSS 盘加本地缓存 -->
<oss_cold_cache>
<type>cache</type>
<disk>oss_cold</disk> <!-- 实际后端是 OSS -->
<path>/var/lib/clickhouse/disks/oss_cache/</path>
<max_size>200Gi</max_size> <!-- 本地缓存大小 -->
<cache_on_write>false</cache_on_write>
</oss_cold_cache>
</disks>
然后把存储策略的 cold 卷指向 oss_cold_cache 磁盘即可。
之后查询 OSS 数据时,首次读取会从 OSS 下载并写入本地缓存,后续读取命中缓存直接本地访问,速度会快很多。
4. 查 OSS 数据的注意事项#
- 数据只读:OSS 上的 part 是只读的,不能直接更新,只能被查询或重新合并。
- 网络依赖:如果 OSS 不可达(网络断了、AK/SK 过期),对冷数据的查询会报错。
- 不要手动删除 OSS 文件:OSS 上的文件结构由 ClickHouse 管理,外部删除会导致查询失败或数据丢失。
- 查询尽量带分区键过滤:
WHERE event_time >= ...可以大大减少需要扫描的 OSS part 数量,降低网络 IO。
5. 如果需要把数据从 OSS 迁回本地#
如果业务变了,想把某些冷分区重新变成热数据(移回本地 default 磁盘),用 ALTER TABLE ... MOVE PARTITION 命令:
ALTER TABLE test.oss_cold_table
MOVE PARTITION '202507' TO DISK 'default';
这样该分区就会从 OSS 下载全部文件并移动到本地磁盘,查询又变回全本地速度。
云厂商托管CLickhouse的数据冷热存储#
利用云厂商自己的冷热存储,无需配置AK/SK。
第一步:确认表已开启冷热分层#
如果 normalized_logs 表还没设置冷热存储策略,先执行:
ALTER TABLE your_database.normalized_logs
MODIFY SETTING storagepolicy = 'hottocold';
这步只需执行一次,开启后数据写入时就会按策略管理。
第二步:设置基于时间的 TTL 迁移规则#
假设你的表有一个时间列(例如 event_time 或 log_date),执行以下语句让超过20天的数据自动迁移到 COS:
ALTER TABLE your_database.normalized_logs
MODIFY TTL event_time + INTERVAL 20 DAY TO VOLUME 'cold';
event_time:替换为你的表实际的时间列名INTERVAL 20 DAY:超过20天的数据将自动迁移TO VOLUME 'cold':指定迁移到冷存储卷(COS)
第三步:验证 TTL 是否生效#
执行以下查询查看 TTL 定义:
SELECT
table,
engine,
ttl_expression,
storage_policy
FROM system.tables
WHERE database = 'your_database' AND table = 'normalized_logs';
再通过 system.parts 确认迁移进度:
SELECT
partition,
disk_name,
rows,
min_time,
max_time
FROM system.parts
WHERE active = 1 AND database = 'your_database' AND table = 'normalized_logs'
ORDER BY min_time;
当 disk_name 显示为 ObjectStorage 时,说明该分区已成功迁移到 COS
Clickhouse底层数据迁移机制#
如果修改了表的TTL规则或存储策略,并不会让老Part自动迁移。只有必须满足以下所有条件时,后台 TTL 任务才会自动移动老 part:
- 表已有 TTL 规则且包含
TO VOLUME/TO DISK; - 老 part 的 TTL 信息已经是最新的(即修改 TTL 后执行过
MATERIALIZE TTL,或 part 是修改后新生成的); - part 满足 TTL 条件(如日期超过 6 个月); 否则,老 part 会一直留在原处。 这是因为当为表设置了ttl之后,该表新创建的part会将这个ttl保存至ttl.txt文档中。在CLickhouse的每个表都有一个后台任务,会定期的扫描这些part的时间,确认了某个part到期了之后,就开始移动。这也是为什么修改了表的ttl之后,在修改ttl之前创建part的ttl不会跟着被修改的原因。这和 DELETE TTL 不一样。DELETE TTL 在 TTL merge 时是会逐行判断的 —— 重写 part、丢掉过期行、留下没过期的。那才会产生"拆分"效果,代价是重写数据。
1. 修改的是 TTL 规则(新增/修改 TO VOLUME / TO DISK)#
ALTER TABLE db.table MODIFY TTL date + INTERVAL 6 MONTH TO VOLUME 'cold';
- 仅修改元数据:已存在的 part 不会立即重新计算 TTL,它们仍保留旧的 TTL 信息。
- 后台 TTL 任务不会自动移动它们,除非 part 参与合并并生成新 part,或者你手动执行
MATERIALIZE TTL。 - 要让老 part 立即应用新 TTL 并移动,需执行:
ALTER TABLE db.table MATERIALIZE TTL;
该操作会遍历所有 part,重新计算 TTL,并触发满足条件的 part 移动到目标卷(或删除过期数据)。
⚠️ 注意:MATERIALIZE TTL 是重操作,会重写 part,消耗大量 IO/CPU,建议在低峰期执行。
2. 修改的是 storage_policy 设置#
ALTER TABLE db.table MODIFY SETTING storage_policy = 'tiered';
- 这只影响新写入的数据和后续合并产生的 part。
- 已有老 part 不会自动迁移到新策略的卷。
- 它们会留在原来的磁盘/卷上,直到:
- 发生合并,且合并后的 part 根据新策略写入新卷(但通常合并会留在原卷,除非有 TTL 移动规则);
- 或者你手动执行
ALTER TABLE ... MOVE PARTITION ... TO VOLUME ...
所有命中 TTL 的历史月份分区都会被排进后台移动队列,每个分区都要从本地盘读、写到 TOS,会持续占用冷存带宽 + 本地盘读 IO + 网络。数据量大时这个过程可能持续数小时到数天,期间对在线查询 / 写入有一定挤压。
大表的迁移#
- 先暂停后台移动
SYSTEM STOP MOVES;
- 手动逐月迁移分区
ALTER TABLE db.table MOVE PARTITION '2023-01' TO VOLUME 'cold';
ALTER TABLE db.table MOVE PARTITION '2023-02' TO VOLUME 'cold';
-- 逐月执行,观察影响
- 调整并发参数
- 降低
background_move_pool_size,减少同时上传的 part 数。 - 限制 OSS 连接数,如
s3_max_connections。
- 降低
- 在低峰期操作
- 避免影响在线查询和写入。
- 监控进度
SELECT * FROM system.moves;
SELECT partition, name, disk_name, volume_name FROM system.parts WHERE active;
- 迁移完成后再启动 TTL 自动管理
SYSTEM START MOVES;
参考文档#
- xx