一、先搞懂啥是冷热分层存储,为啥要在ClickHouse里用

咱们先抛开复杂的术语,举个生活化的例子:你手机相册里,刚拍的照片、上周拍的旅游照是你经常翻的,得存在运行内存或者机身闪存里,打开快;但几年前的毕业照、小时候的老照片,一年都翻不了一次,还占地方,就可以存到便宜的云端相册或者外置硬盘里。

数据库里的冷热分层存储,其实就是这个逻辑。数据也分“常被用”的热数据和“很少碰”的冷数据:热数据要查得快,得存在速度快但贵的存储(比如SSD);冷数据没人碰,存在速度慢但便宜的存储(比如S3、OSS这些对象存储)就行,能省大笔成本。

那为啥要在ClickHouse里搞这个?ClickHouse是专门存海量数据、做快速分析的数据库,比如互联网公司的用户行为数据、电商的交易数据,动不动就攒几十上百TB,要是全存在贵的SSD上,成本能把人吓哭。分层存储就是把冷数据挪去便宜存储,既不影响热数据的查询速度,又能省成本,简直是海量数据场景的“省钱神器”。

二、核心第一步:给ClickHouse搭冷热分层的基础环境

要搞分层,得先有“热存储”和“冷存储”两个地方。咱们先把环境搭起来,这里统一用ClickHouse 22.3版本,搭配MinIO(和S3兼容的对象存储,适合本地测试)当冷存储,技术栈固定为【ClickHouse 22.3 + MinIO 兼容S3对象存储】。

2.1 先装MinIO(冷存储的载体)

MinIO是开源的对象存储,和S3的接口完全兼容,ClickHouse能直接认。咱们用Docker装,方便快捷:

# 拉取MinIO镜像
docker pull minio/minio

# 启动MinIO,注意替换AK/SK、端口这些参数
docker run -d \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio \
  -v /data/minio:/data \
  -e "MINIO_ROOT_USER=admin" \
  -e "MINIO_ROOT_PASSWORD=admin123" \
  minio/minio server /data --console-address ":9001"

装完之后,打开浏览器访问http://localhost:9001,用上面的账号密码登录,创建一个叫clickhouse-cold的桶,这就是咱们放冷数据的地方。

2.2 配置ClickHouse对接MinIO

ClickHouse要识别MinIO,得改它的配置文件。找到ClickHouse的配置目录(比如CentOS下是/etc/clickhouse-server/),编辑config.xml,在<storage_configuration>标签里加下面的配置:

<!-- 冷存储的S3配置 -->
<storage_configuration>
  <disks>
    <!-- 热存储:默认的本地SSD,ClickHouse默认就是这个,不用改 -->
    <default>
      <type>local</type>
      <path>/var/lib/clickhouse/</path>
    </default>
    <!-- 冷存储:对接MinIO的S3磁盘 -->
    <cold_s3>
      <type>s3</type>
      <endpoint>http://localhost:9000/clickhouse-cold/</endpoint>
      <access_key_id>admin</access_key_id>
      <secret_access_key>admin123</secret_access_key>
      <region>us-east-1</region> <!-- 随便填一个合法的S3区域就行 -->
    </cold_s3>
  </disks>
  <!-- 定义存储策略:把热、冷磁盘绑在一起 -->
  <policies>
    <hot_cold_policy>
      <volumes>
        <!-- 热卷:存热数据,用default磁盘 -->
        <hot>
          <disk>default</disk>
        </hot>
        <!-- 冷卷:存冷数据,用cold_s3磁盘 -->
        <cold>
          <disk>cold_s3</disk>
        </cold>
      </volumes>
    </hot_cold_policy>
  </policies>
</storage_configuration>

改完配置,重启ClickHouse服务,让配置生效:

systemctl restart clickhouse-server

三、关键操作1:从热存储迁移旧的冷数据

搭好环境后,咱们得把之前已经存在ClickHouse里的旧数据,按冷热规则迁移到对应的存储里。举个具体的例子:假设咱们有一张用户行为表user_behavior,存了2023年1月到2024年5月的所有行为数据,现在要把2023年的(冷数据)迁移到S3,2024年的(热数据)留在本地。

3.1 先看原来的表结构

原来的表是用MergeTree引擎(ClickHouse最常用的引擎)建的,默认存在本地热存储:

CREATE TABLE user_behavior (
    user_id UInt64,          -- 用户ID
    event_type String,       -- 行为类型(浏览、点击、下单等)
    event_time DateTime,     -- 行为发生时间
    page_url String          -- 访问的页面地址
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time) -- 按月分区,方便管理数据
ORDER BY (user_id, event_time);   -- 排序键,优化查询速度

3.2 给表加存储策略,迁移旧数据

咱们要给这张表指定刚才搭的hot_cold_policy存储策略,然后把2023年的分区(也就是2023年1月到12月的所有数据)迁移到冷存储。具体操作分两步:

第一步,给表添加存储策略。因为MergeTree引擎支持ALTER TABLE修改存储策略,所以直接执行:

ALTER TABLE user_behavior MODIFY SETTING storage_policy = 'hot_cold_policy';

这一步只是告诉ClickHouse,这张表以后可以用热冷存储,还没迁移数据。

第二步,把2023年的分区迁移到冷存储。ClickHouse有专门的ALTER TABLE MOVE PARTITION命令,咱们把2023年的所有分区(格式是202301、202302...202312)移到冷卷:

-- 把2023年的所有分区迁移到冷存储的cold卷
ALTER TABLE user_behavior MOVE PARTITION '202301' TO VOLUME 'cold';
ALTER TABLE user_behavior MOVE PARTITION '202302' TO VOLUME 'cold';
-- ... 一直到202312,也可以批量写,比如:
ALTER TABLE user_behavior MOVE PARTITION IN ('202301','202302','202303','202304','202305','202306','202307','202308','202309','202310','202311','202312') TO VOLUME 'cold';

迁移完之后,咱们可以查一下分区的存储情况,验证是不是成功了:

-- 查user_behavior表的分区信息,看每个分区的存储位置
SELECT partition, disk_name FROM system.parts WHERE table = 'user_behavior' AND database = 'default';

执行结果里,2023年的分区disk_name应该是cold_s3,2024年的是default,说明迁移成功。

四、关键操作2:用TTL自动管理冷热数据

手动迁移旧数据是一次性操作,以后新进来的数据怎么自动按冷热分配?这就要用到TTL(Time To Live,生存时间)策略了。简单说,TTL就是给数据定个“保质期”:数据刚进来时是热的,存在本地;过了一段时间(比如超过3个月没人碰),自动变成冷的,挪去S3;再过很久(比如超过1年),直接删掉,省存储。

4.1 给表加TTL规则

咱们还是拿user_behavior表举例,定两个规则:

  1. 数据超过3个月(90天),自动从本地热存储迁移到S3冷存储;
  2. 数据超过1年(365天),直接删除。

给表加TTL的操作,还是用ALTER TABLE命令:

ALTER TABLE user_behavior MODIFY SETTING
-- 规则1:event_time超过90天,移到冷卷
ttl = event_time + INTERVAL 90 DAY TO VOLUME 'cold',
-- 规则2:event_time超过365天,删除
event_time + INTERVAL 365 DAY DELETE;

这里要注意,TTL是按分区执行的,因为咱们的表是按月分区的,所以每个分区会在分区的event_time超过90天的时候,自动迁移到冷存储。

4.2 验证TTL的效果

TTL是ClickHouse后台自动执行的,默认每隔1小时检查一次。咱们可以手动触发一次TTL,看看效果:

-- 手动触发TTL执行
ALTER TABLE user_behavior MATERIALIZE TTL;

执行完之后,再查分区的存储情况,就能看到超过90天的分区已经移到冷存储了。

五、应用场景、优缺点和注意事项

5.1 适合的应用场景

冷热分层存储不是万能的,只适合下面这些场景:

  • 海量时序数据:比如日志、监控数据、用户行为数据,这些数据越新越常用,越旧越没人碰;
  • 数据有明确的时间属性:比如交易数据、订单数据,能按时间区分冷热;
  • 成本敏感的场景:比如中小公司、创业公司,或者数据量特别大的企业,想省存储成本的。

5.2 技术的优缺点

优点

  • 省成本:S3的存储成本比SSD便宜很多,一般只有SSD的1/5到1/10,数据量越大省的越多;
  • 不影响热数据查询:热数据还是存在本地SSD,冷数据存在S3,两者不干扰;
  • 自动化管理:TTL能自动迁移、删除数据,不用人工操作。

缺点

  • 冷数据查询慢:S3的速度比SSD慢很多,要是偶尔要查冷数据,速度会比热数据慢好几倍;
  • 配置复杂:要对接S3、配置存储策略、TTL,对新手来说有点麻烦;
  • 依赖外部存储:要是S3出问题,冷数据就查不了了。

5.3 注意事项

  • 分区粒度要合适:比如按月分区,要是按天分区,分区太多,ClickHouse管理起来麻烦;
  • TTL时间要合理:比如热数据的时间不能太短,不然经常要查的热数据会被移去冷存储,影响速度;
  • 冷存储要稳定:S3要选靠谱的服务商,或者自己搭稳定的MinIO,避免冷数据丢了;
  • 测试再上线:一定要先在测试环境跑通,再用到生产环境,不然出问题影响业务。

六、总结

冷热分层存储是解决ClickHouse海量数据存储成本问题的有效方案,核心就是把热数据存在贵但快的SSD,冷数据存在便宜但慢的S3,通过存储策略对接、旧数据迁移、TTL自动管理三个步骤落地。整个过程看起来复杂,其实只要按步骤来,就能实现“既快又省”的效果。

最后要提醒大家,分层存储不是必须的,要是数据量不大(比如只有几TB),全存在SSD上也没问题,没必要折腾;但要是数据量上了几十上百TB,分层存储绝对是值得花时间搞的优化。