ClickHouse 数据生命周期管理:TTL 自动过期与冷热分层策略 ClickHouse 数据生命周期管理TTL 自动过期与冷热分层策略一、数据不是越多越好是会花钱的大家好我是朱大喜。上个月我们团队的 ClickHouse 集群告警磁盘使用率到了 87%。DBA 在群里发了一句谁的表在堆数据能不能清一下我打开一看好家伙有个日志表存了 3 年的明细数据占了 4TB 空间。但实际业务查询 90% 集中在近 30 天超过 6 个月的数据基本没人碰过。这就像你在北京三环租了个大仓库里面塞满了三年前的旧报纸——每个月都在为它们付房租。ClickHouse 作为一个列存分析引擎写入快、压缩狠但不意味着你可以无限往里塞数据。数据是有生命周期的热数据需要存在 SSD 上保证查询速度温数据可以挪到 HDD 降低成本冷数据要么归档要么直接删除。ClickHouse 的 TTLTime To Live机制就是为这个场景量身定做的。二、TTL 的三种玩法删、挪、聚合TTL 在 ClickHouse 里可以作用在三个层面表级别、列级别、行级别。不同层面的 TTL 解决不同的问题。graph TB subgraph 数据生命周期 A[数据写入br/当天] -- B[热数据br/SSDbr/近7天] B -- C[温数据br/HDDbr/7-90天] C -- D[冷归档br/对象存储br/90-365天] D -- E[过期删除br/超过365天] end subgraph TTL 策略映射 F[行级TTL: 删除过期行] -- E G[分区TTL: 移动冷热盘] -- B G -- C H[列级TTL: 聚合历史数据] -- D end style B fill:#FF5722,color:#fff style C fill:#FF9800,color:#fff style D fill:#2196F3,color:#fff style E fill:#9E9E9E,color:#fff行级 TTL —— 自动删除过期数据。这是最常用也最简单的场景。比如日志表只保留 90 天超过 90 天的数据自动清理。设置也简单-- 创建一个带行级TTL的表超过90天的数据自动删除 CREATE TABLE app_logs ( event_time DateTime, -- 事件时间TTL以这个字段为基准 user_id UInt64, event_type String, event_data String ) ENGINE MergeTree() PARTITION BY toYYYYMMDD(event_time) -- 按天分区方便整分区删除 ORDER BY (event_type, user_id) TTL event_time INTERVAL 90 DAY; -- 核心event_time超过90天整行删除TTL 清理不是实时触发的而是在后台 Merge 阶段进行的。ClickHouse 会在合并数据分片时检查 TTL 条件符合条件的行直接跳过不写入新分片。这意味着清理有延迟但零性能代价——数据是在 Merge 过程中自然消失的不会阻塞查询。你还可以给 TTL 指定DELETE关键字明确表达意图虽然默认行为就是删除-- 显式指定 DELETE 按日期分区做整分区清理性能最优 TTL event_time INTERVAL 90 DAY DELETE WHERE event_type debug -- 只删除debug日志关键日志保留更久列级 TTL —— 保留行但清理列。有些场景你不想删整行只想清理某些大字段。比如订单表里有个 JSON 格式的detail列存了用户行为路径这个字段 30 天后就没参考价值了但订单的基本信息金额、状态需要保留。-- 列级TTLdetail_json字段30天后自动清空其他列保留 CREATE TABLE orders ( order_id UInt64, order_date Date, amount Decimal(10,2), status String, detail_json String -- 这个字段只保留30天 ) ENGINE MergeTree() PARTITION BY toYYYYMM(order_date) ORDER BY order_id TTL order_date INTERVAL 30 DAY DELETE WHERE 11, -- 这是行级TTL -- detail_json列在order_date30天后被替换为默认值空字符串 detail_json TTL order_date INTERVAL 30 DAY;列级 TTL 的一个妙用是对老数据做自动聚合。比如原始粒度数据保留 30 天超过 30 天的自动聚合成小时级汇总-- 更优雅的做法老数据自动聚合而不是全部丢弃 CREATE TABLE metrics_detail ( ts DateTime, metric_name String, value Float64, tags String ) ENGINE MergeTree() PARTITION BY toYYYYMMDD(ts) ORDER BY (metric_name, ts) TTL ts INTERVAL 30 DAY GROUP BY toStartOfHour(ts) AS ts, -- 聚合到小时粒度 metric_name, sum(value) AS value, AS tags -- tags字段在聚合后丢弃 SETTINGS materialize_ttl_after_modify 0;三、冷热分层SSD 和 HDD 搭配使用如果你的 ClickHouse 节点既有 SSD 又有 HDD最常见的配置是系统盘 SSD 数据盘 HDD 组 RAID可以使用存储策略来实现冷热分层。热数据放 SSD 上保证查询性能冷数据自动迁到 HDD 上节省成本。-- 第一步在 config.xml 中定义存储策略DBA操作 -- storage_configuration -- disks -- ssd -- path/data/ssd/clickhouse//path -- /ssd -- hdd -- path/data/hdd/clickhouse//path -- /hdd -- /disks -- policies -- hot_cold -- volumes -- hot -- diskssd/disk -- max_data_part_size_bytes107374182400/max_data_part_size_bytes -- /hot -- cold -- diskhdd/disk -- /cold -- /volumes -- /hot_cold -- /policies -- /storage_configuration -- 第二步建表时指定存储策略 TTL自动迁移 CREATE TABLE events ( event_time DateTime, user_id UInt64, event_type LowCardinality(String), -- LowCardinality 降低基数类型的存储 properties Map(String, String) -- Map类型适合存动态属性 ) ENGINE MergeTree() PARTITION BY toYYYYMM(event_time) ORDER BY (event_type, user_id) TTL event_time INTERVAL 7 DAY TO VOLUME hot, -- 7天内在SSD event_time INTERVAL 90 DAY TO VOLUME cold; -- 7-90天迁移到HDD这里TO VOLUME指令的神奇之处在于它不是复制数据而是物理上将数据分片从 SSD 目录移动到 HDD 目录。这个迁移同样发生在后台 Merge 过程中不影响查询。冷热分层的成本收益非常可观。以一个 10 节点集群为例如果全部用 SSD 存储每节点配 4TB SSD月成本很高。用冷热分层每节点配 1TB SSD 10TB HDDSSD 只放近 7 天热数据90% 以上的数据落在 HDD 上总存储成本能降低 60% 以上而查询性能只在对冷数据做全量扫描时才受到影响——而这种查询本来就不多见。四、TTL 使用中的三个避坑指南坑一TTL 不是删分区是删行。很多同学以为设置了TTL event_time INTERVAL 90 DAY就万事大吉了ClickHouse 会自动把 90 天前的分区整个删掉。不是的——TTL 是按行判断的而且依赖 Merge 过程。如果你的表写入量不大、Merge 不频繁过期数据可能存留很长时间才被清理。如果想立即释放空间手动ALTER TABLE ... DROP PARTITION才是正解。坑二TTL 表达式里只能用表里的列。你不能写TTL now() - INTERVAL 90 DAY因为now()不是表的列。必须以表中的时间列为基准。这其实是个好设计——防止你不小心写错导致整表数据被清。坑三列级 TTL 清空后数据并没有真的从磁盘消失。ClickHouse 的列级 TTL 是把列值替换为默认值通常是空字符串或 0物理上这些空值仍然占据压缩存储空间。但好消息是ClickHouse 的列存压缩对空值的处理非常高效——连续的空值压缩到几乎为零。所以空间释放效果虽然不如行级 TTL 直接但也足以满足大部分场景。-- 手动触发TTL清理在业务低峰期执行 ALTER TABLE app_logs MATERIALIZE TTL; -- 查看TTL相关信息确认清理前的数据量 SELECT partition, sum(rows) AS rows, formatReadableSize(sum(bytes_on_disk)) AS size, min(event_time) AS min_time, max(event_time) AS max_time FROM system.parts WHERE table app_logs AND active 1 GROUP BY partition ORDER BY partition DESC;五、总结ClickHouse 的 TTL 机制是数据生命周期管理的利器但不同层面的 TTL 有各自的适用场景行级 TTL 适合明确有丢弃时间的日志型数据列级 TTL 适合大字段的成本优化存储策略 TTL 适合冷热分层的成本控制。落地时的优先顺序建议先上行级 TTL解决磁盘被打爆的亡羊补牢问题再上存储策略冷热分层降成本最后考虑列级 TTL 和聚合 TTL精细优化。按照这个顺序逐步推进每一步都能看到立竿见影的效果。最后一句话磁盘空间不是大风刮来的——无论是物理机上的 SSD 还是云上的 EBS都在按 GB 计费。让你的 ClickHouse 只存真正有价值的数据而不是当个垃圾桶什么都往里塞。