4. 数据导入与导出

数据导入导出,说白了就是 ClickHouse 的「进出口」业务。我见过不少新手,建表时信心满满,一到导数据就卡壳。其实这块没那么玄乎,掌握几个核心套路就够了。

今天咱们就把 INSERT INTO、文件导入、客户端导入、SELECT 导入、导出到文件这五板斧,一个一个说清楚。

ClickHouse 数据导入导出核心路径 ClickHouse 表 INSERT INTO VALUES 文件导入 (CSV/JSON/Parquet) clickhouse-client 导入 INSERT SELECT 导出到文件 SELECT INTO OUTFILE 避坑提示 大文件导入 先切分再并行

4.1 INSERT INTO 语法:最基础的写入姿势

INSERT INTO 是 ClickHouse 最直接的写入方式。语法上跟标准 SQL 差不多,但有几个细节你得留神。

-- 标准写法:指定列
INSERT INTO table_name (col1, col2, col3) VALUES (1, 'a', now()), (2, 'b', now());

-- 不指定列:按表结构顺序
INSERT INTO table_name VALUES (1, 'a', now()), (2, 'b', now());

-- 从表达式写入
INSERT INTO table_name SELECT * FROM other_table;
我的习惯:生产环境我永远显式指定列名。为啥?表结构一变更,不指定列名的 SQL 直接崩。别偷这个懒。

ClickHouse 的 INSERT 是异步批量的。你每发一条 INSERT,它不会立刻落盘,而是攒一批再写。这个「攒批」的行为由 max_insert_block_size 控制,默认 1,048,576 行。

注意:ClickHouse 不适合单条 INSERT。你一条一条插,性能极差。我见过有人用循环插 10 万条,跑了半小时。改成一次 INSERT 10 万条,3 秒完事。

4.2 从文件导入:CSV、JSON、Parquet

实际工作中,数据大多躺在文件里。ClickHouse 原生支持三种主流格式:CSV、JSON、Parquet。我个人最常用 Parquet,压缩率高、读取快。

4.2.1 CSV 导入

-- 从 CSV 文件导入
INSERT INTO my_table FORMAT CSV
-- 然后在 clickhouse-client 中粘贴 CSV 内容,或通过管道传入

-- 或者用文件重定向
clickhouse-client --query "INSERT INTO my_table FORMAT CSV" < data.csv

CSV 要注意几个坑:

  • 默认分隔符是逗号,字段内有逗号必须用引号包裹
  • 空值用 \N 表示,不是空字符串
  • 表头行不会自动跳过,需要你手动处理

4.2.2 JSON 导入

-- JSONEachRow:每行一个 JSON 对象
INSERT INTO my_table FORMAT JSONEachRow
{"id":1,"name":"Alice","ts":"2024-01-01"}
{"id":2,"name":"Bob","ts":"2024-01-02"}

-- JSONCompact:紧凑格式
INSERT INTO my_table FORMAT JSONCompact
[1,"Alice","2024-01-01"],[2,"Bob","2024-01-02"]
避坑指南:JSONEachRow 的字段顺序不重要,ClickHouse 按字段名匹配。但 JSONCompact 是按位置匹配的,顺序必须跟表结构一致。我曾经在生产环境搞混过,导入了一堆错位数据,排查了半天。

4.2.3 Parquet 导入

-- 直接导入 Parquet 文件
INSERT INTO my_table SELECT * FROM file('data.parquet', 'Parquet')

-- 或者指定路径
INSERT INTO my_table SELECT * FROM file('/data/export/*.parquet', 'Parquet')

Parquet 是列式存储格式,跟 ClickHouse 天生一对。导入速度比 CSV 快 3-5 倍,而且自带 schema,不容易出错。

格式 导入速度 压缩率 Schema 校验 适用场景
CSV 慢 低 无 小数据、临时调试
JSON 中等 中等 弱(字段名匹配) 日志、API 数据
Parquet 快 高 强(自带类型) 大数据、生产环境

4.3 使用 clickhouse-client 导入

clickhouse-client 是 ClickHouse 自带的命令行工具,导入大文件时特别好用。

# 从文件导入 CSV
clickhouse-client --query "INSERT INTO my_table FORMAT CSV" < data.csv

# 从文件导入 Parquet
clickhouse-client --query "INSERT INTO my_table SELECT * FROM file('data.parquet', 'Parquet')"

# 带压缩的导入
gzip -dc data.csv.gz | clickhouse-client --query "INSERT INTO my_table FORMAT CSV"

# 指定输入格式
clickhouse-client --format_csv_delimiter="|" --query "INSERT INTO my_table FORMAT CSV" < data.csv
我的经验:导入超过 100GB 的文件时,我会先 split 成 1GB 的小文件,然后开 4-6 个 clickhouse-client 并行导入。这样能把导入时间缩短 60% 以上。但注意控制并发数,别把 ClickHouse 搞 OOM 了。

4.4 使用 INSERT SELECT:表间数据流转

INSERT SELECT 是 ClickHouse 最强大的数据导入方式之一。它允许你从一个表查询数据,直接写入另一个表。

-- 基本用法:从 A 表导入 B 表
INSERT INTO target_table SELECT * FROM source_table

-- 带过滤条件
INSERT INTO target_table SELECT * FROM source_table WHERE event_date > '2024-01-01'

-- 跨集群导入
INSERT INTO target_table SELECT * FROM remote('other_host:9000', 'db', 'source_table', 'user', 'password')

-- 聚合后导入
INSERT INTO agg_table SELECT 
    toDate(event_time) as day,
    count() as cnt
FROM raw_table
GROUP BY day

INSERT SELECT 是流式的。源表读一批,目标表写一批,不会把全部数据加载到内存。所以即使源表有几十亿行,也能平稳导入。

注意:INSERT SELECT 默认是事务性的。如果导入过程中出错,已经写入的数据不会回滚。我建议你先用 SELECT count() 确认源表数据量,再执行导入。或者先导入到一个临时表,验证无误后再 rename。

4.5 数据导出到文件

导出跟导入是对称的。ClickHouse 支持把查询结果导出成各种格式。

# 导出为 CSV
clickhouse-client --query "SELECT * FROM my_table" --format CSV > export.csv

# 导出为 JSONEachRow
clickhouse-client --query "SELECT * FROM my_table" --format JSONEachRow > export.json

# 导出为 Parquet
clickhouse-client --query "SELECT * FROM my_table" --format Parquet > export.parquet

# 使用 INTO OUTFILE(在 SQL 中指定)
SELECT * FROM my_table INTO OUTFILE '/tmp/export.csv' FORMAT CSV

# 带压缩导出
clickhouse-client --query "SELECT * FROM my_table" --format CSV | gzip > export.csv.gz

导出时有个小技巧:如果你只需要部分列,别用 SELECT *,显式列出需要的列。这样能减少 IO,导出速度翻倍。

避坑指南:我曾经导出 500GB 数据到 CSV,结果发现 CSV 文件里某些字段包含换行符,导致下游解析全乱了。后来我改用 Parquet 格式,自带 schema 和转义,再没出过问题。所以,生产环境导出优先用 Parquet。

4.6 本章小结

数据导入导出是 ClickHouse 的日常操作,但细节决定成败:

  • 小数据用 INSERT INTO VALUES,简单直接
  • 文件导入优先选 Parquet,又快又稳
  • 大文件用 clickhouse-client 配合管道,记得切分并行
  • 表间流转用 INSERT SELECT,流式处理不占内存
  • 导出用 INTO OUTFILE 或客户端重定向,格式选 Parquet