Article
第1章:ClickHouse 简介与核心特性
1.1 什么是 ClickHouse
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| ClickHouse | 由 Yandex 开发的开源列式数据库管理系统(DBMS),专为在线分析处理(OLAP)设计,支持高性能实时数据分析。 | 不适用于高并发点查或频繁更新的场景。 |
| 列式存储 | 数据按列而非按行存储,极大提升聚合查询效率,减少 I/O。 | 对 OLTP 类事务处理不友好。 |
| 实时查询 | 支持毫秒级响应的复杂分析查询,适用于仪表盘、报表等实时分析场景。 | 查询性能依赖于数据模型设计。 |
| 向量化执行 | 利用 CPU SIMD 指令对整列数据并行处理,显著提升计算速度。 | 需要现代 CPU 支持。 |
| 高压缩比 | 列式存储使相同类型数据连续存储,便于高效压缩,节省存储空间。 | 压缩率受数据重复度影响。 |
1.2 OLAP 与 OLTP 的区别
| 对比维度 | OLTP(联机事务处理) | OLAP(联机分析处理) | 说明 |
|---|---|---|---|
| 主要用途 | 处理日常事务,如订单、支付、用户注册等。 | 支持复杂分析、报表、数据挖掘等决策支持。 | ClickHouse 属于 OLAP 系统。 |
| 数据操作 | 频繁的 INSERT、UPDATE、DELETE(小量数据) | 大量 SELECT 查询,少量批量 INSERT,几乎无 UPDATE/DELETE | ClickHouse 不支持原地 UPDATE/DELETE(除特定引擎)。 |
| 数据量 | 单条记录小,总量中等 | 海量数据,常达 TB/PB 级 | ClickHouse 擅长处理大数据集。 |
| 查询复杂度 | 简单查询,通常基于主键点查 | 复杂查询,涉及多表 JOIN、GROUP BY、聚合、窗口函数等 | ClickHouse 优化了复杂聚合查询。 |
| 响应时间 | 要求毫秒级响应 | 可接受秒级甚至分钟级响应 | ClickHouse 目标是亚秒到秒级响应。 |
| 存储结构 | 行式存储(Row-based) | 列式存储(Column-based) | 列存提升分析效率。 |
| 典型系统 | MySQL, PostgreSQL, Oracle | ClickHouse, Apache Druid, Amazon Redshift | — |
1.3 ClickHouse 的设计哲学与适用场景
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 快速查询 | 优先保证查询速度,牺牲写入灵活性和事务一致性。 | 不适合需要强一致性的业务系统。 |
| 批量写入 | 鼓励批量插入,不推荐频繁小批量写入。 | 小批量写入会导致性能下降和碎片增多。 |
| 无事务支持 | 不支持 ACID 事务,无回滚机制。 | 无法用于银行转账等场景。 |
| 最终一致性 | 在分布式集群中通过异步复制实现数据同步,保证最终一致。 | 查询可能读到旧数据。 |
| 简单数据模型 | 推荐宽表设计,避免复杂 JOIN,鼓励预聚合。 | JOIN 性能较差,应尽量避免。 |
| 适用场景 | 日志分析、用户行为分析、实时监控、BI 报表、广告系统等 OLAP 场景。 | 不适用于电商订单系统等 OLTP 场景。 |
| 不适用场景 | 需要频繁更新、高并发点查、强事务一致性的系统。 | 如需更新,可使用 ReplacingMergeTree。 |
1.4 ClickHouse 的主要优势与局限性
| 类别 | 项目 | 说明 | 注意事项 |
|---|---|---|---|
| 优势 | 极致查询性能 | 列存 + 向量化 + 索引 + 并行处理,实现亚秒级复杂查询。 | 性能依赖合理建模。 |
| 高压缩比 | 列式存储使压缩效率高,节省存储成本。 | 文本列压缩效果更佳。 | |
| 水平扩展 | 支持分布式集群,可通过分片和复制扩展容量与性能。 | 需 ZooKeeper 协调复制。 | |
| 实时数据摄入 | 支持高吞吐写入,适合流式数据接入。 | 写入后不可改,需合理设计 TTL。 | |
| SQL 支持 | 提供类 SQL 接口,学习成本低。 | 部分 SQL 特性不支持(如 FULL JOIN)。 | |
| 局限性 | 不支持事务 | 无 BEGIN/COMMIT,无回滚。 | 不能用于金融核心系统。 |
| 更新删除困难 | 原地 UPDATE/DELETE 不支持,需依赖特定引擎或后台合并。 | 使用 ReplacingMergeTree 需注意版本控制。 | |
| JOIN 性能差 | JOIN 为单线程执行,大表 JOIN 慢。 | 建议使用预聚合宽表替代 JOIN。 | |
| 不支持索引下推 | 部分谓词无法下推到存储层过滤。 | 依赖主键和分区裁剪优化。 | |
| 内存消耗高 | 复杂查询可能消耗大量内存。 | 需合理配置 max_memory_usage。 |
第2章:环境搭建与基础操作
2.1 安装 ClickHouse(本地 & Docker)
| 安装方式 | 命令/步骤 | 用途说明 | 注意事项 |
|---|---|---|---|
| Ubuntu/Debian(APT) | sudo apt-get install apt-transport-https ca-certificates dirmngr | 通过 APT 包管理器安装稳定版 ClickHouse。 | 确保网络可访问包源。 |
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754 | |||
echo "deb https://packages.clickhouse.com/deb stable main" | |||
sudo apt-get update | 安装服务端和客户端。 | 安装后服务自动启动。 | |
sudo apt-get install -y clickhouse-server clickhouse-client | |||
| CentOS/RHEL(YUM) | sudo yum install -y yum-utils | 使用 YUM 安装 RPM 包。 | 适用于 CentOS 7+。 |
sudo yum-config-manager --add-repo https://packages.clickhouse.com/rpm/clickhouse.repo | |||
sudo yum install -y clickhouse-server clickhouse-client | |||
| Docker 安装 | docker run -d --name clickhouse-server -p 8123:8123 -p 9000:9000 clickhouse/clickhouse-server:latest | 启动官方镜像,暴露 HTTP 和 TCP 端口。 | 需持久化 /var/lib/clickhouse 目录。 |
| 自定义配置 | -v /path/to/config.xml:/etc/clickhouse-server/config.xml | 挂载自定义配置文件。 | 修改配置后需重启容器。 |
2.2 启动服务与客户端连接
| 操作 | 命令/语法 | 用途说明 | 注意事项 |
|---|---|---|---|
| 启动服务 | sudo service clickhouse-server start 或 sudo systemctl start clickhouse-server | 启动 ClickHouse 服务进程。 | 确保端口 8123(HTTP)、9000(TCP)未被占用。 |
| 停止服务 | sudo systemctl stop clickhouse-server | 停止服务。 | 建议先停止写入再停服务。 |
| 重启服务 | sudo systemctl restart clickhouse-server | 重启服务。 | 配置修改后需重启生效。 |
| CLI 客户端连接 | clickhouse-client | 使用默认配置连接本地服务。 | 默认用户为 default,无密码。 |
| 指定主机连接 | clickhouse-client --host 127.0.0.1 --port 9000 --user default --password '' | 连接远程或指定实例。 | 确保网络和防火墙允许。 |
| HTTP 接口测试 | curl "http://localhost:8123/" | 测试 HTTP 服务是否运行。 | 返回 Ok. 表示正常。 |
2.3 数据库与表的基本管理命令
| 命令 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| 创建数据库 | CREATE DATABASE IF NOT EXISTS test_db; | 创建新数据库。 | 数据库存储在 /var/lib/clickhouse/ 下。 |
| 删除数据库 | DROP DATABASE IF EXISTS test_db; | 删除数据库及其所有表。 | 慎用,不可恢复。 |
| 查看数据库 | SHOW DATABASES; | 列出所有数据库。 | 系统库如 system 不可删。 |
| 创建表 | CREATE TABLE test_db.users (id UInt32, name String, created DateTime) ENGINE = MergeTree() ORDER BY id; | 在指定数据库创建表。 | 必须指定 ENGINE 和 ORDER BY。 |
| 删除表 | DROP TABLE IF EXISTS test_db.users; | 删除表结构和数据。 | 数据不可恢复。 |
| 查看表结构 | DESCRIBE TABLE test_db.users; 或 SHOW CREATE TABLE test_db.users; | 查看表定义。 | 用于调试和迁移。 |
| 查看所有表 | SHOW TABLES FROM test_db; | 列出数据库中所有表。 | 可省略 FROM 查当前库。 |
2.4 插入、查询、删除基础数据操作
| 操作 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| 插入数据 | INSERT INTO test_db.users (id, name, created) VALUES (1, 'Alice', '2025-01-01 12:00:00'), (2, 'Bob', '2025-01-02 13:00:00'); | 批量插入数据。 | 建议批量插入,避免逐行插入。 |
| 插入查询结果 | INSERT INTO test_db.users_backup SELECT * FROM test_db.users; | 将查询结果插入另一表。 | 两表结构需兼容。 |
| 简单查询 | SELECT * FROM test_db.users; | 查询所有数据。 | 生产环境避免 SELECT *。 |
| 条件查询 | SELECT name FROM test_db.users WHERE id = 1; | 按条件过滤。 | WHERE 条件应利用主键或分区。 |
| 聚合查询 | SELECT count(*) FROM test_db.users WHERE created >= '2025-01-01'; | 统计数量。 | ClickHouse 聚合性能极强。 |
| 删除数据 | ALTER TABLE test_db.users DELETE WHERE id = 1; | 标记删除(异步) | 仅 MergeTree 家族支持,立即返回,后台执行。 |
| 截断表 | TRUNCATE TABLE test_db.users; | 快速清空表数据。 | 比 DELETE 更快,不可条件删除。 |
说明:
- 所有命令均可在
clickhouse-client中执行。ALTER TABLE ... DELETE是异步删除,数据不会立即消失,需等待后台合并。- 生产环境建议使用
ReplacingMergeTree或SummingMergeTree等引擎管理数据更新。
第3章:数据类型详解
3.1 基础数据类型(Int, Float, String, Bool)
| 类型名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 整数类型 | Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64 | 有符号/无符号整数,用于 ID、计数等。 | CREATE TABLE example (user_id UInt32, age Int8) ENGINE=Memory; | 根据取值范围选择最小合适类型以节省空间。 |
| 浮点数 | Float32, Float64 | 单精度和双精度浮点数,用于科学计算、指标存储。 | CREATE TABLE sensor_data (temp Float32, humidity Float64) ENGINE=Memory; | 不适合精确金额计算,建议用整数存储(如分)。 |
| 字符串 | String | 变长字符串,可存储任意长度文本(包括二进制)。 | INSERT INTO users (name) VALUES ('张三'); SELECT * FROM users WHERE name = '李四'; | 无需指定长度,支持 UTF-8。 |
| 布尔值 | Bool | 逻辑真/假,实际是 UInt8 的别名(0 = false, 1 = true)。 | CREATE TABLE flags (active Bool); INSERT INTO flags VALUES (true), (false); | 写入时可使用 true/false 或 1/0。 |
3.2 日期与时间类型(Date, DateTime, DateTime64)
| 类型名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Date | Date | 存储日期(年-月-日),范围 1970-01-01 到 2105-12-31。 | CREATE TABLE logs (event_date Date) ENGINE=Memory; INSERT INTO logs VALUES ('2025-10-01'); | 占用 1 字节,内部为 UInt16。 |
| DateTime | DateTime | 存储日期和时间(精确到秒),范围 1970-2038。 | CREATE TABLE events (ts DateTime) ENGINE=Memory; INSERT INTO events VALUES ('2025-10-01 08:00:00'); | 可指定时区:DateTime('Asia/Shanghai')。 |
| DateTime64 | DateTime64(p, [timezone]), p: 0-9(精度) | 高精度时间戳,支持纳秒级(p=9),推荐用于日志、监控。 | CREATE TABLE metrics (ts DateTime64(3, 'UTC')) ENGINE=Memory; INSERT INTO metrics VALUES ('2025-10-01 08:00:00.123'); | p=3(毫秒)、p=6(微秒)、p=9(纳秒);时区影响显示和转换。 |
3.3 复合数据类型(Array, Tuple, Map, Nested)
| 类型名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 数组 | Array(T) | 存储相同类型的元素列表,T 可为任意类型。 | CREATE TABLE tags_example (user_id UInt32, tags Array(String)) ENGINE=Memory; INSERT INTO tags_example VALUES (1, ['a','b']); | 使用 arrayElement(arr, idx) 或 arr[idx] 访问。 |
| 元组 | Tuple(T1, T2, ...) | 固定长度的异构类型集合,类似结构体。 | CREATE TABLE location (coords Tuple(Float64, Float64)) ENGINE=Memory; INSERT INTO location VALUES ((116.4, 39.9)); | 通常用于临时组合字段,不常用于表结构。 |
| 映射 | Map(KeyType, ValueType) | 键值对集合,KeyType 通常为 String/Int,Value 为任意类型。 | CREATE TABLE props (user_props Map(String, String)) ENGINE=Memory; INSERT INTO props VALUES ({'lang': 'zh', 'theme': 'dark'}); | ClickHouse 21.9+ 支持,需启用 allow_experimental_map_type。 |
| 嵌套数据结构 | Nested(name1 = T1, name2 = T2, ...) | 特殊语法,用于表示多值属性(如用户行为序列),实际生成多个数组列。 | CREATE TABLE user_actions (user_id UInt32, actions Nested(action String, ts DateTime)) ENGINE=Memory; | 查询时需用 ARRAY JOIN 展开。 |
3.4 特殊类型(Nullable, LowCardinality, IPv4/IPv6)
| 类型名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Nullable | Nullable(T) | 允许字段为 NULL,T 为非 Nullable 类型。 | CREATE TABLE profile (email Nullable(String)) ENGINE=Memory; INSERT INTO profile VALUES (NULL); | 性能低于非 Nullable 类型,慎用;避免用于主键。 |
| LowCardinality | LowCardinality(T) | 优化低基数字符串(如状态、国家),使用字典编码压缩存储。 | CREATE TABLE logs (status LowCardinality(String)) ENGINE=Memory; | T 通常为 String 或 Enum;基数 < 10,000 效果好。 |
| IPv4 | IPv4 | 存储 IPv4 地址,语义化并支持网络函数。 | CREATE TABLE access (ip IPv4) ENGINE=Memory; INSERT INTO access VALUES ('192.168.1.1'); | 实际为 UInt32,支持 toIPv4 函数转换。 |
| IPv6 | IPv6 | 存储 IPv6 地址。 | INSERT INTO access6 (ip) VALUES ('2001:db8::1'); | 实际为 FixedString(16)。 |
第4章:表引擎(Table Engines)
4.1 表引擎分类与选择原则
| 分类 | 引擎示例 | 用途说明 | 选择原则 | 注意事项 |
|---|---|---|---|---|
| MergeTree 家族 | MergeTree, ReplacingMergeTree | OLAP 核心引擎,支持大容量、高性能分析。 | 绝大多数分析场景首选。 | 需合理设计 ORDER BY 和 PARTITION BY。 |
| 日志类引擎 | Log, TinyLog, StripeLog | 简单存储,无索引,适合临时表或小数据量测试。 | 数据量小(< 10^6 行),一次性写入。 | 不支持索引,查询全表扫描。 |
| 集成类引擎 | Kafka, MySQL, HDFS, JDBC | 与外部系统集成,用于数据摄取或查询外部数据。 | 实时摄入 Kafka 或查询 MySQL 表。 | Kafka 引擎仅用于消费,需搭配物化视图。 |
| 特殊用途引擎 | Memory, Buffer, Distributed, Dictionary | 内存表、缓冲、分布式查询、字典表等。 | 缓存、中间表、分片聚合等场景。 | Memory 表数据不持久化。 |
| 只读引擎 | File, URL, View | 查询文件或远程数据,不存储数据。 | 临时分析 CSV 文件。 | 不能 INSERT。 |
4.2 MergeTree 家族引擎
| 引擎名称 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| MergeTree | CREATE TABLE mt_example (id UInt32, name String, ts DateTime) ENGINE = MergeTree() ORDER BY id PARTITION BY toYYYYMM(ts); | 最基础的 MergeTree 引擎,支持分区、排序、高效查询。 | 所有 MergeTree 变体的基础;数据按 ORDER BY 排序合并。 |
| ReplacingMergeTree | ENGINE = ReplacingMergeTree(version) ORDER BY (id) | 按指定版本字段合并时保留最新版本,用于去重或更新。 | 合并是异步的,查询需加 FINAL 或使用 GROUP BY;version 可为 UInt 或 DateTime。 |
| SummingMergeTree | ENGINE = SummingMergeTree() ORDER BY (id) SUMMING COLUMN views, clicks | 自动合并时对指定数值列求和,用于预聚合。 | 非 SUMMING 列保留首次插入值;适合统计报表。 |
| AggregatingMergeTree | ENGINE = AggregatingMergeTree() ORDER BY (id) | 存储聚合函数状态(如 AggregateFunction(sum, UInt64)),合并时聚合。 | 需配合物化视图和 -State/-Merge 函数使用;适合复杂聚合。 |
4.3 日志类引擎(TinyLog, StripeLog, Log)
| 引擎名称 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| TinyLog | ENGINE = TinyLog | 最简单引擎,数据分列存储为小文件,无索引。 | 仅适用于单用户、小数据量、一次性写入的临时表。 |
| Log | ENGINE = Log | 类似 TinyLog,但每列一个文件,支持并发读。 | 支持多个查询同时读,但仍不支持索引或并发写。 |
| StripeLog | ENGINE = StripeLog | 将所有数据存储在一个文件中,元数据在另一文件。 | 写入高效,但崩溃时易损坏;适合写一次读多次场景。 |
共同局限:
- 不支持索引,查询需全表扫描。
- 不支持并发写入。
- 不支持 ALTER UPDATE/DELETE。
- 无分区功能。
- 仅用于测试或临时表。
4.4 集成类引擎(Kafka, MySQL, JDBC, HDFS)
| 引擎名称 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| Kafka | ENGINE = Kafka() SETTINGS kafka_broker_list = 'localhost:9092', kafka_topic_list = 'logs', kafka_group_name = 'clickhouse_group', kafka_format = 'JSONEachRow'; | 从 Kafka 主题消费数据,通常与物化视图结合使用。 | 本身不存储数据,仅作为数据源;需物化视图写入目标表。 |
| MySQL | ENGINE = MySQL('host:port', 'database', 'table', 'user', 'password') | 实时查询 MySQL 表,适用于小表 JOIN 或维度表。 | 每次查询都访问 MySQL,性能依赖 MySQL;建议用 Dictionary 引擎缓存。 |
| JDBC | ENGINE = JDBC('jdbc:postgresql://localhost:5432/db', 'schema', 'table') | 通过 JDBC 连接任意数据库(需驱动)。 | 性能较差,仅用于数据迁移或临时查询。 |
| HDFS | ENGINE = HDFS('hdfs://namenode:9000/path', 'TSV') | 直接读写 HDFS 上的文件。 | 支持 TSV, CSV, JSON 等格式;适合批量导入导出。 |
4.5 其他常用引擎(Memory, Dictionary, Distributed)
| 引擎名称 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| Memory | ENGINE = Memory | 数据存储在内存中,重启后丢失。 | 适合临时中间表、缓存小表;不持久化。 |
| Dictionary | ENGINE = Dictionary(dict_name) | 查询预加载的字典表(通常来自 MySQL/Flat 文件)。 | 需先定义字典配置;用于维度表关联,避免 JOIN。 |
| Distributed | ENGINE = Distributed(cluster_name, database, table, [sharding_key]) | 逻辑表,将查询分发到集群各分片并合并结果。 | 不存储数据;需正确配置集群(config.xml);sharding_key 控制写入分片。 |
说明:
- MergeTree 家族是核心,生产环境应优先掌握。
- 集成类引擎多用于数据管道,常与物化视图搭配。
- Distributed 引擎是实现水平扩展的关键。
第5章:SQL 语法与查询操作
5.1 SELECT 查询基础(WHERE, ORDER BY, LIMIT)
| 语法元素 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| SELECT | SELECT [columns] FROM table [WHERE ...] [ORDER BY ...] [LIMIT ...] | 查询数据的基本结构。 | SELECT name, age FROM users WHERE age > 18 ORDER BY age DESC LIMIT 10; | 避免 SELECT *,明确指定列名提升性能。 |
| WHERE | WHERE condition | 过滤满足条件的行。 | SELECT * FROM logs WHERE event_date = '2025-10-01' AND user_id = 1001; | 条件应利用主键或分区键以提升效率。 |
| ORDER BY | ORDER BY expr [ASC|DESC] | 对结果排序。 | SELECT * FROM sales ORDER BY amount DESC, ts ASC; | 多字段排序时注意顺序;大数据集排序消耗内存。 |
| LIMIT | LIMIT [offset, ]n | 限制返回行数,支持分页。 | SELECT * FROM events ORDER BY ts LIMIT 10; LIMIT 10, 20; — 跳过10条取20条 | LIMIT 不保证顺序,需配合 ORDER BY。 |
| LIMIT BY | LIMIT n BY columns | 按分组取每组前 n 行。 | SELECT user_id, product_id, score FROM recommendations ORDER BY score DESC LIMIT 3 BY user_id; | 类似”每用户 top 3 推荐”;需与 ORDER BY 配合。 |
5.2 聚合函数与 GROUP BY
| 函数/语法 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| count() | count(*), count(column) | 统计行数,count(*) 包含 NULL,count(col) 排除 NULL。 | SELECT count(*) FROM users; SELECT count(email) FROM users; | ClickHouse 对 count(*) 有优化。 |
| sum() | sum(column) | 求和,仅支持数值类型。 | SELECT sum(sales) FROM daily_report; | NULL 值自动忽略。 |
| avg() | avg(column) | 计算平均值。 | SELECT avg(duration) FROM sessions; | 返回 Float64 类型。 |
| min()/max() | min(column), max(column) | 求最小/最大值。 | SELECT min(ts), max(ts) FROM logs; | 常用于时间范围分析。 |
| GROUP BY | GROUP BY column(s) | 按列分组进行聚合。 | SELECT country, count(*) FROM users GROUP BY country; | 非聚合列必须出现在 GROUP BY 中。 |
| WITH ROLLUP | GROUP BY ... WITH ROLLUP | 生成小计和总计(层级聚合)。 | GROUP BY a, b WITH ROLLUP; | 生成 (a,b), (a), () 三级聚合。 |
| WITH CUBE | GROUP BY ... WITH CUBE | 生成所有维度组合的聚合。 | GROUP BY a, b WITH CUBE; | 组合数为 2^n,慎用于多列。 |
5.3 DISTINCT、HAVING 与子查询
| 语法元素 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| DISTINCT | SELECT DISTINCT column(s) FROM table | 去重返回唯一值。 | SELECT DISTINCT status FROM orders; | 支持多列去重;大数据集消耗内存。 |
| DISTINCT ON | SELECT DISTINCT ON (col) ... | 按某列去重,保留第一条(需排序)。 | 不支持 | ClickHouse 不支持 DISTINCT ON,可用 GROUP BY 或 ARRAY JOIN 替代。 |
| HAVING | HAVING condition | 对聚合结果进行过滤。 | SELECT dept, avg(salary) FROM employees GROUP BY dept HAVING avg(salary) > 10000; | HAVING 作用于聚合后,WHERE 作用于聚合前。 |
| 子查询 | (SELECT ...) | 在查询中嵌套另一个查询,可用于 FROM, WHERE, IN 等。 | SELECT * FROM users WHERE id IN (SELECT user_id FROM logs WHERE ts > '2025-10-01'); | 子查询在 IN 中性能较差,建议用 JOIN 或临时表替代。 |
| 标量子查询 | (SELECT expr ... LIMIT 1) | 返回单值的子查询,可用于表达式。 | SELECT name, (SELECT max(age) FROM users) AS max_age FROM users; | 必须返回单行单列,否则报错。 |
5.4 JOIN 操作详解
| JOIN 类型 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| INNER JOIN | SELECT ... FROM A INNER JOIN B ON A.id = B.id | 仅返回两表匹配的行。 | SELECT u.name, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id; | ClickHouse JOIN 为单线程,大表 JOIN 慢。 |
| LEFT JOIN | SELECT ... FROM A LEFT JOIN B ON ... | 返回左表所有行,右表无匹配则补 NULL。 | SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id; | 右表应为小表,否则性能极差。 |
| RIGHT JOIN | SELECT ... FROM A RIGHT JOIN B ON ... | 返回右表所有行,左表无匹配则补 NULL。 | 类似 LEFT JOIN,方向相反。 | 建议统一用 LEFT JOIN 避免混淆。 |
| FULL JOIN | FULL OUTER JOIN | 返回两表所有行,无匹配则补 NULL。 | 支持但性能极差。 | 尽量避免使用。 |
| CROSS JOIN | SELECT ... FROM A, B 或 CROSS JOIN | 笛卡尔积,每行组合。 | SELECT a.x, b.y FROM A a, B b; | 结果集巨大,慎用。 |
| ARRAY JOIN | ARRAY JOIN arr | 展开数组列,每元素生成一行。 | SELECT id, action FROM user_actions ARRAY JOIN actions as action; | 用于处理 Nested 类型或 Array 列。 |
JOIN 注意事项:
- ClickHouse 的 JOIN 不支持索引下推,全表扫描右表。
- 推荐使用预聚合宽表替代 JOIN。
- 小维度表可使用 Dictionary 引擎缓存。
5.5 UNION ALL 与查询组合
| 操作 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| UNION ALL | (SELECT ...) UNION ALL (SELECT ...) | 合并多个查询结果,不去重。 | (SELECT 'log' AS type, ts, msg FROM app_log) UNION ALL (SELECT 'error', ts, error_msg FROM errors) ORDER BY ts; | ClickHouse 仅支持 UNION ALL,不支持 UNION(去重)。 |
| 多查询组合 | 多个 SELECT 用 UNION ALL 连接 | 实现分表查询合并或多源数据整合。 | 同上 | 各子查询列数和类型必须一致。 |
| 使用场景 | — | 常用于按时间分区的表合并查询。 | (SELECT * FROM events_202509) UNION ALL (SELECT * FROM events_202510); | 可替代 Distributed 引擎的本地查询。 |
5.6 窗口函数(Window Functions)
| 函数 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| row_number() | row_number() OVER (PARTITION BY ... ORDER BY ...) | 为每行分配唯一序号。 | SELECT name, dept, salary, row_number() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees; | 常用于”每部门 top N”查询。 |
| rank() | rank() OVER (...) | 排名,相同值并列,后续跳号。 | rank() OVER (ORDER BY score DESC) | 如 90,90,80 → 1,1,3。 |
| dense_rank() | dense_rank() OVER (...) | 密集排名,相同值并列,后续不跳号。 | dense_rank() OVER (ORDER BY score DESC) | 如 90,90,80 → 1,1,2。 |
| lag() / lead() | lag(col, n, default) OVER (...) | 获取前/后 n 行的值。 | lag(price, 1) OVER (ORDER BY ts) AS prev_price | 用于计算环比、差值。 |
| sum() OVER | sum(col) OVER (PARTITION BY ... ORDER BY ...) | 累计求和。 | sum(sales) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING) | 支持 ROWS / RANGE 窗口。 |
| OVER 子句 | OVER ([PARTITION BY] [ORDER BY] [frame]) | 定义窗口范围。 | OVER (PARTITION BY user ORDER BY ts ROWS 3 PRECEDING) | 必须包含 ORDER BY 才能使用 frame。 |
窗口函数注意:
- ClickHouse 22.6+ 支持完整窗口函数。
- 内存消耗高,大数据集需调优
max_memory_usage。- 不支持 WINDOW 命名窗口。
第6章:数据定义与表结构管理
6.1 CREATE TABLE 语法详解
| 语法组件 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 基本结构 | CREATE TABLE [IF NOT EXISTS] [db.]table (...) ENGINE = ... | 创建表的基本语法。 | CREATE TABLE IF NOT EXISTS analytics.visits (user_id UInt32, url String, ts DateTime) ENGINE = MergeTree() ORDER BY (user_id, ts); | 必须指定 ENGINE 和 ORDER BY。 |
| 列定义 | column_name Type [DEFAULT expr|MATERIALIZED expr|ALIAS expr] | 定义列及其默认值或表达式。 | created DateTime DEFAULT now(), date Date DEFAULT toDate(ts), revenue Float64 MATERIALIZED price * qty; | MATERIALIZED 列不接受 INSERT,由表达式生成。 |
| 主键 | PRIMARY KEY expr | 显式指定主键(可选,ORDER BY 已包含主键功能)。 | PRIMARY KEY (user_id) | 通常与 ORDER BY 一致,可省略。 |
| 分区 | PARTITION BY expr | 按表达式分区,如按月、按地区。 | PARTITION BY toYYYYMM(ts) | 分区过多(> 1000)会影响元数据性能。 |
| 排序键 | ORDER BY tuple_expr | 指定数据在分区内的排序方式。 | ORDER BY (region, city, ts) | 决定索引结构和查询性能。 |
| 索引 | INDEX index_name expr TYPE type [...] GRANULARITY n | 创建跳数索引(如 minmax, set, bloom filter)。 | INDEX idx_url url TYPE bloom_filter(0.01) GRANULARITY 1; | 需在 CREATE TABLE 中定义;GRANULARITY 为索引粒度。 |
6.2 ALTER TABLE 修改表结构
| 操作 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 添加列 | ALTER TABLE t ADD COLUMN col Type [AFTER col_name] | 新增列。 | ALTER TABLE visits ADD COLUMN referrer String AFTER url; | 新列默认为 NULL 或 DEFAULT 值。 |
| 删除列 | ALTER TABLE t DROP COLUMN col | 删除列。 | ALTER TABLE visits DROP COLUMN temp_field; | 删除列是元数据操作,后台异步清理数据。 |
| 修改列类型 | ALTER TABLE t MODIFY COLUMN col NewType | 修改列数据类型。 | ALTER TABLE visits MODIFY COLUMN user_id UInt64; | 类型必须兼容(如 Int32 → Int64),否则报错。 |
| 重命名列 | ALTER TABLE t RENAME COLUMN old TO new | 重命名列。 | ALTER TABLE visits RENAME COLUMN ts TO timestamp; | 仅修改名称,不影响数据。 |
| 添加索引 | ALTER TABLE t ADD INDEX idx ... GRANULARITY n | 添加跳数索引。 | ALTER TABLE visits ADD INDEX idx_city city TYPE minmax GRANULARITY 1; | MergeTree 家族支持;需后台合并生效。 |
| 删除索引 | ALTER TABLE t DROP INDEX idx_name | 删除索引。 | ALTER TABLE visits DROP INDEX idx_city; | 仅删除索引文件,不影响数据。 |
| 修改 ORDER BY / PARTITION BY | 不支持 | — | — | ClickHouse 不支持直接修改 ORDER BY 或 PARTITION BY,需重建表。 |
ALTER 注意事项:
- 所有 ALTER 操作异步执行,立即返回。
- 大表修改可能耗时较长,建议在低峰期操作。
6.3 PARTITION 与 PART 操作
| 操作 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 查看分区 | SELECT partition, name FROM system.parts WHERE table = 't' | 查看表的分区和分片信息。 | SELECT partition, part, rows FROM system.parts WHERE table = 'visits'; | system.parts 是核心监控表。 |
| 分区删除 | ALTER TABLE t DROP PARTITION id | 删除整个分区。 | ALTER TABLE visits DROP PARTITION 202510; | 立即删除,不可恢复。 |
| 分区分离 | ALTER TABLE t DETACH PARTITION id | 将分区移出表(数据保留,逻辑分离)。 | ALTER TABLE visits DETACH PARTITION 202509; | 可用于备份或修复。 |
| 分区附加 | ALTER TABLE t ATTACH PARTITION id | 将已分离的分区重新加入表。 | ALTER TABLE visits ATTACH PARTITION 202509; | 数据必须存在且格式正确。 |
| 分区合并 | ALTER TABLE t MATERIALIZE PARTITION id | 强制合并分区内的数据段(parts)。 | ALTER TABLE visits MATERIALIZE PARTITION 202510; | 通常由后台自动合并,紧急时手动触发。 |
| 删除分段 | ALTER TABLE t DROP PART 'part_name' | 删除指定分段(part)。 | ALTER TABLE visits DROP PART '202510_1_5_2'; | 高级操作,一般用 DROP PARTITION。 |
6.4 TTL(数据生命周期管理)
| 语法 | 语法格式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 行级 TTL | TTL ts [DELETE|TO DISK|TO VOLUME] | 基于时间删除行或将数据迁移至其他存储。 | CREATE TABLE logs (...) TTL event_date + INTERVAL 30 DAY; | 默认行为是 DELETE。 |
| 存储迁移 | TTL ts TO DISK 'disk_name' | 将过期数据迁移到冷存储(如 HDD、S3)。 | TTL ts TO DISK 'hdd', ts + INTERVAL 1 DAY TO VOLUME 's3'; | 需预先配置存储策略(storage_configuration)。 |
| 多级 TTL | 多个 TTL 规则 | 定义分层生命周期策略。 | TTL ts + INTERVAL 7 DAY TO DISK 'hdd', ts + INTERVAL 30 DAY TO VOLUME 's3', ts + INTERVAL 365 DAY DELETE; | 按顺序匹配,第一个满足条件的规则生效。 |
| 列级 TTL | TTL col_ttl 在列定义中 | 仅对特定列设置过期时间。 | detail_log String TTL event_date + INTERVAL 10 DAY | 该列数据过期后被清除,其他列保留。 |
| 修改 TTL | ALTER TABLE t MODIFY TTL ... | 修改现有表的 TTL 策略。 | ALTER TABLE logs MODIFY TTL event_date + INTERVAL 60 DAY; | 可动态调整生命周期。 |
TTL 注意事项:
- TTL 由后台合并线程执行,非实时。
TO DISK/VOLUME需在config.xml中定义存储策略。
6.5 索引与排序键(PRIMARY KEY, ORDER BY, INDEX)
| 概念 | 说明 | 语法示例 | 注意事项 |
|---|---|---|---|
| 排序键 (ORDER BY) | 决定数据在分区内的物理排序,是 ClickHouse 性能核心。 | ORDER BY (site_id, event_date, user_id) | 前缀列用于快速范围查询和去重。 |
| 主键 (PRIMARY KEY) | 可选,若未指定则默认等于 ORDER BY 前缀。 | PRIMARY KEY site_id | 实际作用与 ORDER BY 重叠,常省略。 |
| 跳数索引 (Skip Index) | 在排序基础上建立稀疏索引,加速非主键列过滤。 | INDEX idx_status status TYPE set(10) GRANULARITY 2; | GRANULARITY 表示每多少个 granule(默认 8192 行)建一个索引项。 |
| MinMax 索引 | 记录每个 granule 的最小最大值,用于范围过滤。 | INDEX idx_ts ts TYPE minmax | 默认为所有列自动创建,无需显式定义。 |
| Set 索引 | 存储每个 granule 中某列的唯一值集合。 | INDEX idx_country country TYPE set(100) | 适用于低基数列的 IN 查询。 |
| Bloom Filter 索引 | 概率型索引,判断值是否存在,减少扫描。 | INDEX idx_email email TYPE bloom_filter(0.01) GRANULARITY 1; | 误判率可调(0.01=1%),适合高基数列。 |
索引设计原则:
- ORDER BY 应包含高频查询的过滤和分组字段。
- 跳数索引对非排序键列的过滤有帮助,但增加存储和写入开销。
- 避免对高基数列(如 UUID)创建 set 索引。
说明:
- 所有 ALTER 和 TTL 操作均为异步,需通过
system.mutations监控进度。- ORDER BY 是性能关键,设计不当会导致查询缓慢。
第7章:高性能查询优化
7.1 数据分区与分片策略
| 策略类型 | 说明 | 语法/配置示例 | 用途 | 注意事项 |
|---|---|---|---|---|
| 分区(Partitioning) | 按逻辑维度(如时间)将表划分为多个目录,提升查询效率和管理粒度。 | PARTITION BY toYYYYMM(event_date) 或 PARTITION BY region | 减少扫描数据量,支持按分区删除/附加。 | 分区不宜过多(建议 < 1000),避免元数据压力。 |
| 分片(Sharding) | 将数据分布到多个物理节点(分片),实现水平扩展。 | 在 Distributed 引擎中指定:ENGINE = Distributed(cluster, db, table) | 提升写入吞吐和查询并发能力。 | 需配置 config.xml 中的 remote_servers 集群定义。 |
| 分片键(Sharding Key) | 决定数据写入哪个分片的表达式。 | INSERT INTO dist_table ... 分布式表写入时使用:DISTRIBUTED(table, shard_key) | 控制数据分布均匀性。 | 推荐使用 rand() 或 user_id % shard_num 避免热点。 |
| 分区合并策略 | 控制后台合并行为,优化性能。 | 在表引擎设置中:SETTINGS index_granularity = 8192, merge_with_ttl_timeout = 3600 | 调整合并频率和粒度。 | 大分区可调大 merge_with_ttl_timeout 减少合并压力。 |
最佳实践:
- 分区粒度建议为月或日(如
toYYYYMMDD(ts))。- 分片数建议为 2 的幂次(如 2, 4, 8),便于负载均衡。
7.2 排序键与查询性能关系
| 概念 | 说明 | 代码示例 | 对查询性能的影响 | 注意事项 |
|---|---|---|---|---|
| 排序键 (ORDER BY) | 决定数据在分区内的物理存储顺序,是 ClickHouse 索引的基础。 | ORDER BY (site_id, event_date, user_id) | 前缀匹配的查询(如 WHERE site_id=1)极快;支持跳数索引。 | 排序键应包含高频过滤和分组字段。 |
| 主键 (PRIMARY KEY) | 可选,若未指定则默认等于 ORDER BY 前缀。 | PRIMARY KEY (site_id) | 实际作用与 ORDER BY 重叠,常省略。 | 推荐直接使用 ORDER BY 定义主键逻辑。 |
| 索引粒度 (Index Granularity) | 每 8192 行(默认)生成一个索引项,用于快速定位数据块。 | SETTINGS index_granularity = 4096 | 粒度越小,索引越精细,但存储开销越大。 | 通常无需修改,默认 8192 为最佳平衡点。 |
| 查询前缀匹配 | 查询条件应匹配 ORDER BY 的前缀列。 | WHERE site_id=1 AND event_date='2025-10-01' ✅ WHERE user_id=1001 ❌(非前缀) | 前缀匹配可大幅减少扫描行数。 | 避免跳过前导列进行过滤。 |
性能建议:
- ORDER BY 列顺序:高基数 → 低基数,高频过滤 → 低频。
- 避免在排序键中包含 String 大字段,影响排序效率。
7.3 物化视图(Materialized View)
| 特性 | 说明 | 语法示例 | 注意事项 |
|---|---|---|---|
| 自动更新 | 物化视图绑定源表,源表写入时自动触发数据写入。 | CREATE MATERIALIZED VIEW mv_sales ENGINE = SummingMergeTree() ORDER BY (region, product) AS SELECT region, product, sum(amount) AS total FROM sales_raw GROUP BY region, product; | 数据写入是异步的,有轻微延迟。 |
| 存储数据 | 物化视图本身是一张物理表,可独立查询。 | SELECT * FROM mv_sales; | 可使用任意表引擎(如 MergeTree, SummingMergeTree)。 |
| 不支持 UPDATE/DELETE | 无法手动修改物化视图数据。 | — | 数据由源表自动填充,不能直接 INSERT。 |
| 多级物化视图 | 可链式构建多层聚合。 | mv1 → mv2 → mv3 | 层级越多延迟越高,需评估必要性。 |
| 替代方案:TO + INSERT TRIGGER | 先建目标表,再建 MV 指向它。 | CREATE TABLE target (...); CREATE MATERIALIZED VIEW mv TO target AS SELECT ...; | 更灵活,可复用表结构。 |
使用场景:
- 实时聚合(如每分钟 UV)
- 宽表预计算
- 数据格式转换(JSON → 结构化)
7.4 投影(Projections)
| 特性 | 说明 | 语法示例 | 注意事项 |
|---|---|---|---|
| 自动选择最优路径 | 查询时自动选择能加速执行的投影。 | CREATE TABLE t (a Int32, b Int32, c String, PROJECTION p1 { SELECT a, b, sum(c) GROUP BY a, b }) ENGINE = MergeTree() ORDER BY a; | 投影数据在后台合并时生成。 |
| 无需改写查询 | 用户仍使用原表查询,系统自动优化。 | SELECT a, b, sum(length(c)) FROM t GROUP BY a, b; ✅ | 查询需匹配投影定义的结构。 |
| 增加写入开销 | 每次写入需更新主表和所有投影。 | — | 投影不宜过多(建议 ≤ 3 个),避免写入性能下降。 |
| 支持多种投影类型 | 如聚合投影、排序投影等。 | 可定义不同 ORDER BY 的投影。 | 实验性功能(22.8+),生产环境需测试。 |
限制:
- 投影为实验性功能,需启用
allow_experimental_projection_optimization。- 不支持所有表引擎,仅 MergeTree 家族支持。
7.5 查询执行计划分析(EXPLAIN)
| EXPLAIN 类型 | 语法 | 输出内容 | 用途 | 示例 |
|---|---|---|---|---|
| EXPLAIN | EXPLAIN SELECT ... | 查询的执行计划树(Pipeline) | 查看操作符顺序、并行度。 | EXPLAIN SELECT count(*) FROM users; |
| EXPLAIN AST | EXPLAIN AST SELECT ... | 抽象语法树(AST) | 调试查询解析过程。 | EXPLAIN AST SELECT 1+1; |
| EXPLAIN PLAN | EXPLAIN PLAN SELECT ... | 逻辑执行计划(计划节点) | 分析优化器决策。 | EXPLAIN PLAN SELECT * FROM t WHERE a=1; |
| EXPLAIN PIPELINE | EXPLAIN PIPELINE SELECT ... | 执行流水线(线程、处理器) | 分析并行度和性能瓶颈。 | EXPLAIN PIPELINE SELECT sum(x) FROM huge_table; |
解读建议:
- 关注
ExpressionTransform、AggregatingTransform等关键节点。- Pipeline 中查看是否并行执行(
max_threads)。- 若 Filter 出现在早期,说明索引生效。
7.6 向量化执行与函数优化
| 优化机制 | 说明 | 优化示例 | 注意事项 |
|---|---|---|---|
| 向量化执行 | 一次处理多个数据(SIMD),而非逐行。 | ClickHouse 内部自动优化 | 所有标量函数均支持向量化,无需手动干预。 |
| 函数性能排序 | 同功能函数性能差异大。 | equals(x,1) > x=1, like > match > position | 优先使用高性能函数。 |
| 避免标量子查询 | 标量子查询逐行执行,性能差。 | ❌ SELECT (SELECT max(x) FROM t) FROM large_table | 改用 CROSS JOIN 或预计算。 |
| 使用 -Map / -Array 函数 | 批量处理集合。 | arrayMap(x -> x*2, arr) | 比 ARRAY JOIN + WHERE 更快。 |
| 正则优化 | 使用 position 替代 match 若仅判断存在。 | position(url, 'abc') > 0 ✅ match(url, 'abc') ❌ | position 性能更高。 |
| 字符串比较 | 使用 equals 替代 = | equals(status, 'active') | equals 是向量化优化函数。 |
性能口诀:
- 能用 IN 不用 OR
- 能用 PARTITION 不用 WHERE
- 能用预聚合不用实时计算
第8章:数据导入与导出
8.1 使用 INSERT 导入数据
| 方法 | 语法示例 | 用途 | 注意事项 |
|---|---|---|---|
| 单行插入 | INSERT INTO users (id, name) VALUES (1, 'Alice'); | 测试或极小量数据。 | 生产环境禁止使用,性能极差。 |
| 批量插入 | INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie'); | 小批量导入(< 1万行)。 | 每批建议 1万~10万行。 |
| 插入查询结果 | INSERT INTO summary SELECT site, count(*) FROM logs GROUP BY site; | ETL 或聚合写入。 | 源表和目标表可跨库。 |
| 指定格式插入 | INSERT INTO t FORMAT JSONEachRow {"a":1,"b":"x"} {"a":2,"b":"y"} | 通过 API 或脚本导入。 | 常用于程序化数据摄入。 |
INSERT 原则:
- ClickHouse 适合批量写入,避免高频小批量。
- 每秒写入次数建议 < 10 次。
8.2 从文件导入(CSV, TSV, JSONEachRow 等)
| 文件格式 | 导入语法 | 说明 | 注意事项 |
|---|---|---|---|
| CSV | INSERT INTO t FORMAT CSV(然后输入数据) | 逗号分隔,最常见。 | 第一行是否为 header 由 format_csv_with_names 控制。 |
| TSV | INSERT INTO t FORMAT TSV(制表符分隔) | ClickHouse 默认格式,性能好。 | 推荐用于脚本导入。 |
| JSONEachRow | INSERT INTO t FORMAT JSONEachRow {"col1":1,"col2":"a"} {"col1":2,"col2":"b"} | 每行一个 JSON 对象,适合日志。 | 不支持嵌套数组自动展开。 |
| Parquet | INSERT INTO t SELECT * FROM file('data.parquet', 'Parquet', 'col1 Int32, col2 String') | 高效列式格式,适合大数据。 | 需文件在服务器本地或 HDFS。 |
| ORC | 类似 Parquet | Hadoop 生态常用。 | 支持有限,建议转 Parquet。 |
文件导入建议:
- 大文件使用
clickhouse-client --query重定向。- 使用
file()表函数可直接查询文件。
8.3 使用 clickhouse-client 批量导入
| 方法 | 命令示例 | 用途 | 注意事项 |
|---|---|---|---|
| 标准输入导入 | `cat data.csv | clickhouse-client —query=“INSERT INTO t FORMAT CSV”` | 从管道导入。 |
| 文件重定向 | clickhouse-client --query="INSERT INTO t FORMAT TSV" < data.tsv | 从文件导入。 | 文件需在客户端机器。 |
| 执行查询并导入 | clickhouse-client --query="SELECT * FROM remote_table" > backup.tsv | 导出数据。 | 可结合 FORMAT 指定输出格式。 |
| 批量执行 SQL 文件 | clickhouse-client < script.sql | 执行建表、插入等脚本。 | 适合初始化操作。 |
| 远程连接导入 | clickhouse-client --host remote --query="INSERT INTO t ..." < data.json | 向远程服务器导入。 | 需网络可达和权限。 |
性能建议:
- 使用
--max_insert_block_size=100000控制块大小。- 开启
--format_csv_delimiter="|"自定义分隔符。
8.4 导出数据到文件或远程系统
| 方法 | 语法示例 | 用途 | 注意事项 |
|---|---|---|---|
| 重定向到文件 | clickhouse-client --query="SELECT * FROM t" > output.csv | 简单导出。 | 输出格式默认 TSV。 |
| 指定格式导出 | clickhouse-client --query="SELECT * FROM t FORMAT CSVWithNames" > report.csv | 带表头 CSV。 | 支持 CSV, JSON, Parquet 等。 |
| 导出到 HDFS | INSERT INTO TABLE FUNCTION hdfs('hdfs://namenode:9000/clickhouse/output', 'TSV') SELECT * FROM t; | 直接写入 HDFS。 | 需 Hadoop 配置支持。 |
| 导出到 S3 | INSERT INTO TABLE FUNCTION s3('https://s3.amazonaws.com/bucket/file.csv', 'CSV') SELECT * FROM t; | 写入 S3,适合备份。 | 需配置 AWS 凭证或 IAM。 |
| 使用 INTO OUTFILE | ClickHouse 不支持 INTO OUTFILE | — | 必须通过客户端重定向或 TABLE FUNCTION。 |
导出建议:
- 大数据量分批导出(
LIMIT + OFFSET)。- 使用
--compression_codec压缩输出(如 gz)。
8.5 与外部系统集成(Kafka, S3, MySQL)
| 集成方式 | 配置/语法示例 | 用途 | 注意事项 |
|---|---|---|---|
| Kafka 引擎 | ENGINE = Kafka() SETTINGS kafka_broker_list='k1:9092,k2:9092', kafka_topic_list='logs', kafka_format='JSONEachRow'; | 实时消费 Kafka 数据。 | 必须搭配物化视图写入目标表。 |
| S3 表函数 | SELECT * FROM s3('https://bucket.s3.amazonaws.com/data.csv', 'CSV', 'col1 Int32') | 查询 S3 上的文件。 | 适合一次性分析或 ETL。 |
| S3 引擎表 | CREATE TABLE s3_table (...) ENGINE = S3(...) | 将 S3 映射为表。 | 支持读写,可用于冷热分层。 |
| MySQL 引擎 | ENGINE = MySQL('host:3306', 'db', 'table', 'user', 'pass') | 实时查询 MySQL 表。 | 每次查询都访问 MySQL,性能差;建议用 Dictionary 缓存。 |
| JDBC 引擎 | ENGINE = JDBC('jdbc:postgresql://host/db', 'schema', 'table') | 连接任意 JDBC 数据库。 | 性能差,仅用于迁移。 |
| HDFS 引擎 | ENGINE = HDFS('hdfs://nn:9000/path', 'TSV') | 读写 HDFS 文件。 | 需 Hadoop 环境支持。 |
集成最佳实践:
- Kafka → ClickHouse:Kafka Engine + MV → MergeTree
- S3 冷数据:S3 引擎表 + TTL 分层存储
- MySQL 维度表:Dictionary 引擎缓存
总结:
- 查询优化核心是:分区 + 排序键 + 物化视图 + 投影。
- 数据导入应遵循:批量写入、避免高频 INSERT、善用
clickhouse-client。- 外部集成推荐:Kafka 实时摄入、S3 冷存储、Dictionary 缓存维度表。
第9章:分布式架构与集群部署
9.1 分布式表(Distributed Engine)原理
| 概念 | 说明 | 语法示例 | 用途 | 注意事项 |
|---|---|---|---|---|
| Distributed 引擎 | 逻辑表,将查询分发到多个分片的本地表,并合并结果。 | CREATE TABLE dist_table (...) ENGINE = Distributed(cluster_name, database, local_table, sharding_key); | 实现跨节点查询和写入。 | 不存储数据,仅路由请求。 |
| 分片(Shard) | 集群中的一个数据节点或节点组,存储部分数据。 | 在 config.xml 中定义。 | 水平扩展数据容量和查询负载。 | 可配置副本(replica)实现高可用。 |
| 副本(Replica) | 同一数据在多个节点上的拷贝,用于容灾和读负载均衡。 | 每个 shard 可包含多个 replica。 | 提升可用性和读性能。 | 写入时由 Distributed 表自动分发。 |
| sharding_key | 决定数据写入哪个分片的表达式。 | rand(), intHash32(user_id), user_id % 2 | 控制数据分布。 | 应保证数据均匀,避免热点。 |
| 查询路由 | SELECT 查询被广播到所有相关分片,结果在协调节点合并。 | SELECT count(*) FROM dist_table; | 用户无感知,透明访问集群数据。 | 大查询可能增加协调节点内存压力。 |
工作流程:
- 写入:
INSERT INTO dist_table→ 按sharding_key路由到目标 shard 的本地表- 查询:
SELECT * FROM dist_table→ 广播到所有 shard → 合并结果返回
9.2 集群配置(config.xml 与 macros)
| 配置项 | 说明 | XML 示例 | 注意事项 |
|---|---|---|---|
| remote_servers | 定义集群拓扑,包括分片和副本。 | <remote_servers><cluster_2s2r><shard><replica><host>node1</host><port>9000</port></replica><replica><host>node2</host><port>9000</port></replica></shard><shard><replica><host>node3</host><port>9000</port></replica><replica><host>node4</host><port>9000</port></replica></shard></cluster_2s2r></remote_servers> | 必须在所有节点的 config.xml 中一致。 |
| macros | 用变量替代重复配置,实现模板化部署。 | <macros><shard>01</shard><replica>node1</replica></macros> | 在 Distributed 表中使用 {shard}, {replica}。 |
| zookeeper | 用于副本协调、元数据同步。 | <zookeeper><node><host>zk1</host><port>2181</port></node></zookeeper> | 多节点部署建议 3~5 个 ZooKeeper 实例。 |
| interserver_http_port | 节点间通信端口(默认 9009)。 | <interserver_http_port>9009</interserver_http_port> | 防火墙需放行。 |
| 分布式 DDL 队列 | 支持在集群执行 ON CLUSTER 命令。 | CREATE TABLE t ON CLUSTER cluster_2s2r ... | 依赖 ZooKeeper 和 macros 配置。 |
配置建议:
- 所有节点
config.xml保持一致(除 macros 外)。- 使用
ON CLUSTER简化集群 DDL 操作。
9.3 数据分片与复制
| 模式 | 说明 | 配置方式 | 优点 | 缺点 |
|---|---|---|---|---|
| 仅分片(Sharding Only) | 数据分布到多个节点,无副本。 | 每个 shard 仅一个 replica。 | 成本低,写入吞吐高。 | 单点故障,数据丢失风险高。 |
| 仅复制(Replication Only) | 所有节点存储全量数据。 | 单 shard 多 replica。 | 查询快,高可用。 | 存储成本高,写入放大全量。 |
| 分片 + 复制(Sharding + Replication) | 每个分片有多个副本,标准生产架构。 | 多 shard,每 shard 多 replica。 | 高可用、高吞吐、可扩展。 | 配置复杂,依赖 ZooKeeper。 |
| 分片策略 | 如何将数据分配到分片。 | sharding_key:rand(), hash(user_id) | 均匀分布避免热点。 | 错误策略导致负载不均。 |
| 复制组(Replication Group) | 一组副本构成一个高可用单元。 | 由 ReplicatedMergeTree 自动管理。 | 故障自动切换。 | 需监控副本同步状态。 |
最佳实践:
- 生产环境必须使用分片 + 复制模式。
- 分片数 = 节点数 / 副本数(如 4节点2副本 → 2分片)。
9.4 ReplicatedMergeTree 引擎详解
| 特性 | 说明 | 语法示例 | 注意事项 |
|---|---|---|---|
| 副本同步 | 基于 ZooKeeper 实现多副本数据一致性。 | ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/visits', '{replica}') ORDER BY ts; | 路径中 {shard} 和 {replica} 来自 macros。 |
| 异步复制 | 写入一个副本后,异步同步到其他副本。 | — | 有轻微延迟(秒级),非强一致。 |
| 自动恢复 | 节点宕机重启后自动同步缺失数据。 | — | 依赖 ZooKeeper 记录日志。 |
| 合并协调 | 后台合并(Merge)操作在副本间协调。 | — | 避免重复合并,节省资源。 |
| ZooKeeper 路径 | 每个表在 ZooKeeper 中有独立路径。 | /clickhouse/tables/01/visits | 路径必须全局唯一。 |
| 监控副本状态 | 查看同步延迟和错误。 | SELECT * FROM system.replicas WHERE table = 'visits'; | 关注 is_leader, queue_size, log_max_index。 |
ReplicatedMergeTree 注意事项:
- 必须配置 ZooKeeper。
- 表引擎替换:MergeTree → ReplicatedMergeTree。
- 不支持 ALTER 修改 ORDER BY,需重建表。
9.5 集群管理与监控
| 工具/命令 | 用途 | 示例 | 注意事项 |
|---|---|---|---|
| system.clusters | 查看集群配置和节点状态。 | SELECT * FROM system.clusters WHERE cluster = 'cluster_2s2r'; | 确认所有节点在线。 |
| system.replicas | 监控副本同步状态。 | SELECT table, is_leader, queue_size, absolute_delay FROM system.replicas; | absolute_delay > 0 表示延迟。 |
| system.parts | 查看分片和分区信息。 | SELECT partition, name, rows, disk_name FROM system.parts WHERE active; | 监控数据分布和大小。 |
| system.mutations | 查看 ALTER 等异步操作进度。 | SELECT mutation_id, command, parts_to_do, is_done FROM system.mutations; | 大表修改需长时间完成。 |
| system.metrics | 实时性能指标(线程、内存、网络)。 | SELECT * FROM system.metrics WHERE metric LIKE '%Query%'; | 快速定位性能瓶颈。 |
| system.query_log | 查询日志,分析慢查询。 | SELECT query, elapsed, read_rows FROM system.query_log WHERE type = 'QueryFinish' ORDER BY elapsed DESC LIMIT 10; | 需启用 log_queries = 1。 |
| ZooKeeper CLI | 手动检查 ZooKeeper 状态。 | `echo stat | nc zk1 2181` |
运维建议:
- 定期检查
system.replicas中的queue_size和absolute_delay。- 使用 Prometheus + Grafana 接入
clickhouse-exporter实现可视化监控。
第10章:函数库详解
10.1 数值函数(四则运算、舍入、随机数)
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| 四则运算 | +, -, *, /, % | 基础计算 | SELECT 10 + 5, 10 % 3; | / 为浮点除,intDiv(a,b) 为整除。 |
| abs() | abs(x) | 绝对值 | abs(-10) → 10 | 支持整数和浮点。 |
| round() | round(x, N) | 四舍五入到 N 位小数 | round(3.14159, 2) → 3.14 | N 可为负数(如 round(123, -1) → 120)。 |
| floor() | floor(x) | 向下取整 | floor(3.9) → 3 | |
| ceil() | ceil(x) | 向上取整 | ceil(3.1) → 4 | |
| rand() | rand() | 生成 0~4294967295 随机数 | rand() % 100 → 0~99 | 常用于 ORDER BY rand() 采样。 |
| intDiv() | intDiv(a, b) | 整数除法 | intDiv(7, 2) → 3 | 避免浮点误差。 |
10.2 字符串函数(拼接、截取、正则匹配)
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| concat() | concat(s1, s2, ...) | 字符串拼接 | concat('a', 'b') → ‘ab’ | 任一参数为 NULL 返回 NULL。 |
| substring() | substring(s, start, length) | 截取子串 | substring('hello', 2, 3) → ‘ell’ | 起始位置从 1 开始。 |
| length() | length(s) | 字节长度 | length('你好') → 6 | lengthUTF8() 为字符数(→2)。 |
| lower()/upper() | lower(s) | 大小写转换 | upper('abc') → ‘ABC’ | |
| trim() | trim(BOTH ' ' FROM s) | 去除空格 | trim(' abc ') → ‘abc’ | 支持 LEADING, TRAILING。 |
| position() | position(haystack, needle) | 查找子串位置 | position('hello', 'll') → 3 | 找不到返回 0。 |
| match() | match(s, pattern) | 正则匹配(布尔) | match('abc123', '[a-z]+') → 1 | 性能较差,优先用 position。 |
| extract() | extract(s, pattern) | 提取正则匹配部分 | extract('id=123', 'id=(\\d+)') → ‘123’ | 仅返回第一组捕获。 |
10.3 日期时间函数(计算、格式化、时区处理)
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| now() | now() | 当前时间(DateTime) | SELECT now(); | 返回 YYYY-MM-DD HH:MM:SS。 |
| today() | today() | 今日日期(Date) | today() → 2025-10-01 | |
| toYYYYMM() | toYYYYMM(date) | 格式化年月 | toYYYYMM(now()) → 202510 | 常用于分区。 |
| dateDiff() | dateDiff('unit', d1, d2) | 计算时间差 | dateDiff('day', '2025-01-01', now()) | 单位:‘second’, ‘minute’, ‘hour’, ‘day’。 |
| dateAdd() | dateAdd('unit', N, date) | 时间加减 | dateAdd('day', -7, today()) → 一周前 | |
| formatDateTime() | formatDateTime(dt, '%F %T') | 自定义格式化 | formatDateTime(now(), '%Y-%m-%d') | 支持 strftime 格式符。 |
| toTimeZone() | toTimeZone(dt, 'Asia/Shanghai') | 时区转换 | toTimeZone(now(), 'UTC') | 需安装时区数据。 |
| toDate() | toDate(expr) | 转换为 Date 类型 | toDate('2025-10-01') |
10.4 条件与逻辑函数(if, multiIf, case)
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| if() | if(cond, then, else) | 三元判断 | if(x>0, 'pos', 'non-pos') | then 和 else 类型需兼容。 |
| multiIf() | multiIf(c1, r1, c2, r2, ..., else) | 多条件分支 | multiIf(score>=90, 'A', score>=80, 'B', 'C') | 比 CASE 更简洁。 |
| CASE | CASE WHEN c1 THEN r1 WHEN c2 THEN r2 ELSE r END | 标准 SQL 条件 | 同 multiIf | 支持 CASE col WHEN val THEN ...。 |
| and/or/not | and, or, not | 逻辑运算 | a=1 AND b=2 | 短路求值。 |
| coalesce() | coalesce(x, y, ...) | 返回第一个非 NULL 值 | coalesce(name, 'Unknown') | 常用于处理缺失值。 |
| assumeNotNull() | assumeNotNull(nullable_col) | 强制转为非 Nullable | assumeNotNull(maybe_name) | 若为 NULL 行为未定义,慎用。 |
10.5 数组与映射函数
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| array() | array(1,2,3) | 创建数组 | SELECT [1,2,3]; | 或用 [] 语法。 |
| has() | has(arr, x) | 判断元素是否存在 | has([1,2,3], 2) → 1 | |
| arrayJoin() | arrayJoin(arr) | 展开数组为多行 | SELECT arrayJoin([1,2]) → 两行 | 常用于 ARRAY JOIN 子句。 |
| arrayMap() | arrayMap(x -> x*2, arr) | 数组映射 | arrayMap(x -> x+1, [1,2]) → [2,3] | 支持 lambda 表达式。 |
| arrayFilter() | arrayFilter(x -> x>0, arr) | 数组过滤 | arrayFilter(x -> x>1, [0,1,2]) → [2] | |
| map() | map('a',1,'b',2) | 创建 Map | map('k1', 'v1', 'k2', 'v2') | 键值对交替。 |
| mapContains() | mapContains(m, 'k1') | 判断 Map 是否含某键 | mapContains(map('a',1), 'a') → 1 | |
| transform() | transform(arr, x -> x*2) | 同 arrayMap |
10.6 聚合函数(count, sum, avg, groupArray 等)
| 函数 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| count() | count(*), count(col) | 行数统计 | count(*), count(email) | count(*) 有优化。 |
| sum() | sum(x) | 求和 | sum(sales) | 忽略 NULL。 |
| avg() | avg(x) | 平均值 | avg(score) | 返回 Float64。 |
| min()/max() | min(x), max(x) | 最值 | min(ts), max(price) | |
| uniq() | uniq(x) | 近似去重计数 | uniq(user_id) | 基于 HyperLogLog,误差约 1%。 |
| uniqExact() | uniqExact(x) | 精确去重计数 | uniqExact(user_id) | 内存消耗高。 |
| groupArray() | groupArray(x) | 聚合为数组 | groupArray(name) BY dept | 可指定长度 groupArray(10)。 |
| groupUniqArray() | groupUniqArray(x) | 去重聚合为数组 | groupUniqArray(tag) | |
| any() | any(x) | 取任意一个值 | any(status) BY order_id | 常用于非聚合列。 |
10.7 高级函数(窗口函数、近似计算、地理函数)
| 函数类别 | 函数 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| 窗口函数 | row_number() OVER (PARTITION BY ... ORDER BY ...) | 排名、分页 | row_number() OVER (ORDER BY score DESC) | 22.6+ 支持。 |
| 近似计算 | approxTopK(N)(x) | 近似 top-N | approxTopK(3)(city) | 基于 CountMinSketch。 |
| 近似计算 | quantile(0.5)(x) | 近似中位数 | quantile(0.95)(duration) | 默认使用 quantiles 可一次计算多个。 |
| 地理函数 | greatCircleDistance(lon1, lat1, lon2, lat2) | 计算两点球面距离(米) | greatCircleDistance(0,0, 1,1) | 需经纬度为 Float64。 |
| 地理函数 | pointInPolygon((x,y), [(0,0),(1,0),(1,1),(0,1)]) | 判断点是否在多边形内 | pointInPolygon((0.5,0.5), square) | 多边形为数组。 |
| 机器学习 | linearRegression() | 线性回归 | SELECT linearRegression(x,y)(10) | 实验性功能。 |
| 位运算 | bitAnd(), bitShiftLeft() | 位操作 | bitShiftLeft(1, 3) → 8 | 用于高效编码。 |
高级函数提示:
- 近似函数(
uniq,quantile)适合大数据场景,牺牲精度换性能。- 地理函数可用于用户地理围栏、距离分析。
总结:
- 分布式部署核心是:Distributed 表 + ReplicatedMergeTree + ZooKeeper + macros。
- 函数库是 ClickHouse 灵活性的体现,掌握常用函数可大幅提升开发效率。
- 生产环境务必监控
system.replicas和system.query_log。
第11章:安全与权限管理
11.1 用户管理(CREATE USER, ALTER USER)
| 命令 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| CREATE USER | CREATE USER [IF NOT EXISTS] user_name [ON CLUSTER cluster] [IDENTIFIED WITH {plaintext_password | sha256_password | double_sha1_password} BY 'password'] [DEFAULT ROLE role_name] [SETTINGS ...] | 创建新用户并设置认证方式。 | CREATE USER alice IDENTIFIED WITH sha256_password BY 'secret123'; | 默认使用 plaintext_password,推荐 sha256_password;用户名区分大小写。 |
| ALTER USER | ALTER USER user_name [IDENTIFIED BY 'new_password'] [DEFAULT DATABASE db_name] [SETTINGS ...] | 修改用户密码、默认数据库等。 | ALTER USER alice IDENTIFIED BY 'newpass456'; | 可修改认证方式;不影响当前会话。 |
| DROP USER | DROP USER [IF EXISTS] user_name | 删除用户 | DROP USER bob; | 删除用户后其权限自动失效;建议先 REVOKE 再删除。 |
| SHOW USERS | SHOW USERS | 列出所有用户 | SHOW USERS; | 需 SHOW USERS 权限;显示用户名和配置。 |
| 用户配置文件(Profile) | 在 users.xml 或 SQL 中定义 | 控制资源使用(如最大并发、内存) | ALTER USER alice SETTINGS max_memory_usage = 1000000000, max_execution_time = 60; | 可通过 profiles 配置模板;用于租户隔离。 |
最佳实践:
- 使用
sha256_password加密存储密码。- 为不同应用创建独立用户,避免共用 default 用户。
- 通过
ON CLUSTER在集群所有节点创建用户。
11.2 角色与权限分配(GRANT, REVOKE)
| 命令 | 语法 | 用途 | 示例 | 注意事项 |
|---|---|---|---|---|
| CREATE ROLE | CREATE ROLE [IF NOT EXISTS] role_name [ON CLUSTER cluster] | 创建角色 | CREATE ROLE analyst; | 角色可跨用户复用;支持 ON CLUSTER。 |
| GRANT(权限) | GRANT privilege_type [(column_list)] ON {db.table | * | db.*} TO {user | role} | 授予系统或对象权限 | GRANT SELECT, INSERT ON sales.* TO analyst; | 可指定列级权限;支持通配符 *。 |
| GRANT(角色) | GRANT role_name TO user_name | 将角色分配给用户 | GRANT analyst TO alice; | 用户可拥有多个角色;支持 WITH ADMIN OPTION。 |
| REVOKE | REVOKE privilege_type ON db.table FROM user | 收回已授予权限 | REVOKE INSERT ON sales.orders FROM alice; | 不影响通过角色继承的权限;需用户重新登录生效。 |
| SHOW GRANTS | SHOW GRANTS FOR user_name | 查看用户权限 | SHOW GRANTS FOR alice; | 显示直接授权和角色继承的权限。 |
常用权限类型:
| 权限 | 说明 | 示例 | 注意事项 |
|---|---|---|---|
| SELECT | 查询表 | GRANT SELECT ON t TO u; | — |
| INSERT | 插入数据 | GRANT INSERT ON t TO u; | — |
| ALTER | 修改表结构 | GRANT ALTER ON t TO u; | DDL 操作需此权限 |
| DROP | 删除表 | GRANT DROP ON t TO u; | 危险权限,慎用 |
| CREATE TABLE | 创建表 | GRANT CREATE TABLE ON db.* TO u; | — |
| SYSTEM | 执行 SYSTEM 命令 | GRANT SYSTEM TO u; | 如 SYSTEM RELOAD CONFIG |
| ALL PRIVILEGES | 所有权限 | GRANT ALL ON *.* TO u; | 等同于 root,禁止生产使用 |
权限设计原则:
- 最小权限原则:只授予必要权限。
- 使用角色分组权限(如 analyst, admin, readonly)。
- 定期审计权限:
SELECT * FROM system.grants;
11.3 行级安全与列级访问控制
| 控制类型 | 实现方式 | 说明 | 示例 | 注意事项 |
|---|---|---|---|---|
| 列级访问控制 | GRANT SELECT(col1, col2) ON t TO u | 限制用户只能查询指定列 | GRANT SELECT(id, name) ON users TO readonly_user; | 用户执行 SELECT * FROM users 仍会返回所有列,但未授权列值为 NULL;需在 users.xml 中启用 allow_insecure → false。 |
| 行级安全(Row Level Security) | 基于 SETTINGS constraints 或应用层过滤 | ClickHouse 原生不支持,需变通实现 | 方案1:视图过滤 CREATE VIEW v_sales AS SELECT * FROM sales WHERE region = 'CN'; GRANT SELECT ON v_sales TO user; | 推荐使用视图(VIEW)实现逻辑行过滤;结合 CONTEXTUALIZED_USER 函数(实验性)动态过滤。 |
| 动态行过滤 | 使用 hasToken() 或自定义字典 | 根据用户身份动态过滤数据 | 假设用户标签存储在 user_tags 字典:SELECT * FROM logs WHERE has(user_tags(current_user()), 'admin'); | 需维护用户-标签映射;性能开销较大。 |
| 敏感数据脱敏 | 在视图中使用 mask() 函数 | 隐藏敏感信息(如手机号) | CREATE VIEW v_users AS SELECT id, mask(phone) AS phone FROM raw_users; | mask('13812345678') → ‘138****5678’ |
限制:
- ClickHouse 无原生 RLS,必须通过视图 + 权限控制实现。
- 列级控制在
SELECT *下仍暴露列名。
11.4 SSL 与网络加密配置
| 配置项 | 说明 | 配置要点 | 注意事项 |
|---|---|---|---|
| 启用 HTTPS | 为 HTTP 接口启用 TLS | 配置 https_port, tls_certificate_file, tls_key_file | 需有效证书(可自签名);客户端使用 https:// 访问。 |
| TCP SSL 加密 | 为 tcp_port_secure 启用加密 | 配置 tcp_port_secure, tcp_secure | 客户端使用 clickhouse-client --secure;禁用非加密 tcp_port 提升安全性。 |
| ZooKeeper SSL | 加密与 ZooKeeper 的通信 | 配置 zookeeper.ssl | 需 ZooKeeper 启用 SSL;verificationMode 可为 none, peer, host。 |
| 内部通信加密 | 节点间复制流量加密 | 同 tcp_secure 配置 | ReplicatedMergeTree 表复制走此通道;生产环境强烈建议启用。 |
| 证书管理 | 使用 Let’s Encrypt 或内部 CA | — | 定期更新证书;避免使用自签名证书在生产环境。 |
安全建议:
- 外网部署必须启用 SSL。
- 防火墙限制 9000(tcp)、8123(http)、9009(interserver)端口访问。
- 使用
require_secure_transport=1强制加密连接。
第12章:监控、运维与调优
12.1 系统表(system.tables, system.parts 等)
| 系统表 | 用途 | 关键字段 | 查询示例 | 注意事项 |
|---|---|---|---|---|
| system.tables | 所有表信息 | database, name, engine, is_distributed | SELECT database, name, engine FROM system.tables WHERE database NOT IN ('system'); | 查看表引擎类型;识别分布式表。 |
| system.parts | 表分区和数据部分 | table, partition, rows, bytes_on_disk, active | SELECT table, partition, rows, bytes_on_disk FROM system.parts WHERE active AND database='logs'; | active=1 表示有效部分;监控数据增长。 |
| system.columns | 所有列信息 | database, table, name, type, default_kind | SELECT * FROM system.columns WHERE table='events'; | 检查列类型和默认值。 |
| system.disks | 存储磁盘信息 | name, path, free_space, total_space | SELECT name, free_space/1024/1024/1024 AS free_gb FROM system.disks; | 监控磁盘使用率;支持多磁盘配置。 |
| system.metrics | 实时性能指标 | metric, value, description | SELECT * FROM system.metrics WHERE metric LIKE '%Query%'; | Query:当前正在执行的查询数;MemoryTracking:总内存使用。 |
| system.events | 累计事件计数 | event, value | SELECT event, value FROM system.events ORDER BY value DESC LIMIT 10; | SelectQuery, InsertQuery;IOBufferAllocs 内存分配次数。 |
| system.processes | 当前正在执行的查询 | user, query, elapsed, read_rows, memory_usage | SELECT user, query, elapsed, read_rows FROM system.processes; | 实时监控慢查询;可 KILL QUERY 终止。 |
运维脚本建议:
- 定期巡检
system.parts碎片数(active 部分过多需优化)。- 监控
system.disks防止磁盘写满。
12.2 查询日志与性能分析
| 日志类型 | 配置 | 用途 | 查询示例 | 注意事项 |
|---|---|---|---|---|
| query_log | 在 config.xml 中启用:<query_log><database>system</database><table>query_log</table><flush_interval_milliseconds>7500</flush_interval_milliseconds></query_log> | 记录所有查询的执行信息 | SELECT query, formatReadableTimeDelta(query_duration_ms/1000), formatReadableSize(read_bytes) AS read, formatReadableSize(memory_usage) AS mem FROM system.query_log WHERE event_date = today() AND query_kind = 'SelectQuery' AND query_duration_ms > 1000 ORDER BY query_duration_ms DESC LIMIT 10; | 分析慢查询;识别资源消耗大户。 |
| part_log | — | 记录数据部分的创建、合并、删除 | 分析 Merge 性能瓶颈 | system.part_log |
| trace_log | 需 SET send_logs_level = 'trace' | 记录查询执行的调用栈 | 深度性能分析,定位热点函数 | 高开销,仅调试使用。 |
| EXPLAIN PIPELINE | EXPLAIN PIPELINE SELECT ... | 查看查询执行流水线 | EXPLAIN PIPELINE SELECT sum(bytes) FROM system.parts; | 查看并行度(max_threads);识别瓶颈节点(如 AggregatingTransform)。 |
| 设置采样日志 | SET log_queries=1; SET log_query_threads=1; | 控制日志粒度 | — | 默认开启,影响轻微。 |
性能分析流程:
- 从
query_log找出慢查询- 使用 EXPLAIN 分析执行计划
- 检查
system.parts是否扫描过多数据- 优化排序键或分区策略
12.3 资源限制与配额管理
| 限制类型 | 配置方式 | 说明 | 示例 | 注意事项 |
|---|---|---|---|---|
| 用户级限制 | 在 users.xml 或 ALTER USER 中设置 | 控制单个用户资源使用 | ALTER USER alice SETTINGS max_memory_usage = 2000000000, max_execution_time = 120, max_concurrent_queries = 5; | max_memory_usage:单查询内存上限;max_execution_time:超时时间(秒)。 |
| 配额(Quota) | 在 users.xml 中定义 | 按时间窗口限制资源使用 | 可定义每小时/天的查询次数、资源消耗上限 | 适用于多租户场景。 |
| 合并限制 | SETTINGS 中控制后台任务 | 防止 Merge 影响查询性能 | ALTER TABLE t MODIFY SETTING number_of_free_entries_in_pool_to_execute_mutation = 1; | background_pool_size:后台线程数;merge_tree_max_rows_to_use_cache:合并缓存阈值。 |
| 网络带宽 | 操作系统层面限流 | 控制节点间同步速度 | 使用 tc 命令或云平台 QoS | 避免复制流量占满带宽。 |
资源管理建议:
- 为 default 用户设置合理限制,防止误操作拖垮集群。
- 使用配额防止某个应用耗尽资源。
12.4 备份与恢复策略
| 方法 | 说明 | 命令示例 | 优点 | 缺点 |
|---|---|---|---|---|
| ALTER TABLE … FREEZE | 创建本地快照(硬链接) | ALTER TABLE logs FREEZE;(快照位于 /var/lib/clickhouse/shadow/) | 快速(硬链接);无需停机 | 仅本地存储;需手动清理 shadow 目录。 |
| cp/rclone + FREEZE | 结合云存储工具 | ALTER TABLE t FREEZE; rclone copy /var/lib/clickhouse/shadow/... s3:backup/ | 可备份到 S3、HDFS 等;成本低 | 恢复需手动操作;元数据需单独备份。 |
| clickhouse-backup | 第三方工具(推荐) | clickhouse-backup create backup1; clickhouse-backup upload backup1; clickhouse-backup download backup1; clickhouse-backup restore backup1 | 支持全量/增量;支持 S3/GCS;自动化 | 需额外部署;学习成本。 |
| 分布式表 + S3 | 冷数据归档 | CREATE TABLE cold ENGINE = S3(...) AS SELECT * FROM hot WHERE event_date < today() - 90; | 实现热冷分层;降低成本 | 查询性能下降;需应用层路由。 |
| 逻辑备份(INSERT SELECT) | 导出数据再导入 | `clickhouse-client —query=“SELECT * FROM t FORMAT Native” > dump.native; cat dump.native | clickhouse-client —query=“INSERT INTO t_restore FORMAT Native”` | 格式通用;跨版本兼容 |
备份策略建议:
- 每日:
clickhouse-backup create+ upload 到 S3- 每周:全量备份
- 恢复演练:定期测试恢复流程
12.5 常见问题排查
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
| 查询极慢或超时 | 扫描数据过多、排序键设计不佳、内存不足 | 1. EXPLAIN PIPELINE SELECT ... 2. SELECT read_rows, memory_usage FROM system.query_log ORDER BY query_duration_ms DESC 3. SELECT * FROM system.parts WHERE table='t' | 优化 WHERE 条件匹配排序键前缀;增加 max_memory_usage;调整分区策略。 |
| 节点无法加入集群 | ZooKeeper 连接失败、macros 配置错误、网络不通 | 1. `echo stat | nc zk1 21812.SELECT * FROM system.clusters WHERE cluster=‘my_cluster’3.ping node2` |
| Replication 延迟 | 网络延迟、合并队列积压、ZooKeeper 性能瓶颈 | 1. SELECT * FROM system.replicas WHERE table='replicated_table' 2. SELECT * FROM system.replication_queue | 检查 absolute_delay;增加 background_pool_size;优化大分区合并策略。 |
| 磁盘写满 | 数据增长过快、shadow 目录未清理、日志文件过大 | 1. df -h 2. `du -sh /var/lib/clickhouse/* | sort -rh` |
| INSERT 写入失败 | 表不存在、权限不足、数据格式错误 | 1. SHOW TABLES LIKE 't' 2. SHOW GRANTS FOR current_user() 3. 检查客户端错误日志 | 确认表名和数据库;授予 INSERT 权限;验证数据格式(如 CSV 分隔符)。 |
| Distributed 表查询无结果 | 本地表无数据、sharding_key 错误、节点宕机 | 1. SELECT count() FROM local_table 2. SELECT * FROM system.clusters 3. SELECT host_name, errors_count FROM system.replicas | 确认数据已写入本地表;检查集群配置;重启失败节点。 |
排查流程:
- 查看错误日志:
/var/log/clickhouse-server/clickhouse-server.log- 检查系统表:
system.errors,system.query_log- 验证配置和网络
- 逐步缩小问题范围
总结:
- 安全:通过用户 + 角色 + SSL 构建多层防护。
- 运维:依赖系统表 +
query_log+ 监控实现可观测性。- 备份:推荐使用
clickhouse-backup工具实现自动化。- 调优:从查询日志 → 执行计划 → 数据分布逐层分析。
第13章:实战案例与最佳实践
13.1 实时日志分析系统构建
| 项目 | 说明 | 实施细节 | 注意事项 |
|---|---|---|---|
| 场景描述 | 收集 Nginx、应用日志,实现实时错误监控、访问趋势分析。 | 日志量:10万条/秒;查询延迟要求:< 1秒;数据保留:30天 | 高吞吐、低延迟、结构化查询 |
| 数据采集层 | 使用 Filebeat + Kafka 或 Vector | 1. Filebeat 监控日志文件 2. 输出到 Kafka 主题 logs-raw 3. ClickHouse 消费 Kafka | Kafka 提供缓冲,防写入雪崩;使用 JSON 格式便于解析。 |
| 数据模型设计 | 分层建模:原始层(Kafka Engine)→ 明细层(ReplicatedMergeTree)→ 聚合层(AggregatingMergeTree) | CREATE TABLE logs_raw (...) ENGINE = Kafka(...) → CREATE TABLE logs_detail (...) ENGINE = ReplicatedMergeTree(...) ORDER BY (ts, ip) → CREATE MATERIALIZED VIEW mv_logs TO logs_detail AS SELECT ... FROM logs_raw | ORDER BY (ts, ip) 支持按时间、IP 高效过滤;物化视图自动消费 Kafka 数据。 |
| 查询优化 | 分区策略 + 索引粒度 + TTL | ENGINE = ReplicatedMergeTree(...) PARTITION BY toYYYYMM(ts) ORDER BY (ts, ip) TTL ts + INTERVAL 30 DAY SETTINGS index_granularity = 8192 | 月分区减少查询扫描;TTL 自动清理旧数据。 |
| 典型查询 | 实时错误率 + 恶意 IP 检测 | SELECT toStartOfHour(ts), countIf(status>=500)/count() FROM logs_detail WHERE ts > now() - 3600 GROUP BY 1 | 使用 countIf 替代 sum(if()) 更高效。 |
| 监控与告警 | 结合 Prometheus + Alertmanager | 监控 Kafka 消费延迟;监控 system.query_log 中 5xx 错误突增 | 设置告警规则:错误率 > 1% 持续 5 分钟。 |
最佳实践:
- 使用 Kafka Engine + 物化视图实现无缝流式摄入。
- 原始数据保留短周期(如 1 小时),明细数据保留 30 天。
- 对 status, path 等高频过滤字段建立数据跳过索引(如 MINMAX)。
13.2 用户行为分析平台
| 项目 | 说明 | 实施细节 | 注意事项 |
|---|---|---|---|
| 场景描述 | 分析 App/网站用户点击流,计算留存、漏斗、路径等指标。 | 事件类型:page_view, click, purchase;用户标识:user_id, device_id;分析维度:渠道、版本、地区 | 支持复杂行为序列分析。 |
| 数据模型 | 宽表设计 user_events | CREATE TABLE user_events (event_date Date, event_time DateTime, user_id String, device_id String, event_type Enum8('page_view'=1, 'click'=2, 'purchase'=3), page String, referrer String, os String, country String, duration UInt32, properties Map(String, String)) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(event_date) ORDER BY (event_date, user_id, event_time) | ORDER BY (event_date, user_id, event_time) 支持按用户会话分析;Map 存储动态属性,避免频繁 ALTER TABLE。 |
| 留存率 | 使用 retention() 函数 | SELECT user_id, retention(event_date = '2025-09-01', event_date = '2025-09-02', event_date = '2025-09-03') AS r FROM user_events GROUP BY user_id HAVING r.1 = 1 | retention() 返回数组,1 表示留存。 |
| 漏斗分析 | 多步事件转化 | SELECT uniqIf(user_id, step1) AS pv, uniqIf(user_id, step2) AS click, uniqIf(user_id, step3) AS purchase FROM (SELECT user_id, max(event_type = 'page_view') AS step1, max(event_type = 'click') AS step2, max(event_type = 'purchase') AS step3 FROM user_events WHERE event_date = '2025-09-01' GROUP BY user_id) | 使用 uniqIf 避免重复计数。 |
| 用户路径 | 使用 arrayJoin + windowFunnel | SELECT level(windowFunnel(3600)(event_time, event_type = 'page_view', event_type = 'click', event_type = 'purchase')) AS conversion_level, count() AS count FROM user_events GROUP BY conversion_level; | windowFunnel 在指定时间窗口内匹配事件序列。 |
| 性能优化 | 数据跳过索引 + PREWHERE | 对 event_type, country 建立数据跳过索引;使用 PREWHERE event_date = '2025-09-01' WHERE country = 'CN' | PREWHERE 比 WHERE 更早执行,减少扫描。 |
最佳实践:
- 使用 Enum 类型存储固定事件类型,节省存储和提升查询速度。
- 复杂分析可预先聚合到物化视图(如每日漏斗表)。
- 用
windowFunnel替代自定义路径匹配,性能更优。
13.3 与 Grafana 集成做可视化
| 项目 | 说明 | 实施步骤 | 注意事项 |
|---|---|---|---|
| 集成架构 | Grafana → ClickHouse 插件 → ClickHouse 集群 | 1. 安装 Grafana 2. 安装 ClickHouse 数据源插件 3. 配置数据源连接 | 支持 SQL 查询和变量。 |
| 数据源配置 | 在 Grafana 中添加 ClickHouse 数据源 | URL: http://clickhouse-server:8123;用户名/密码:grafana_user;安全:启用 SSL | 为 Grafana 创建专用只读用户;限制 max_execution_time 防止长查询拖垮集群。 |
| 创建仪表盘 | 设计监控面板 | 添加 Panel → 选择 ClickHouse 数据源 → 编写查询:SELECT toStartOfFiveMinute(event_time) AS time, count() AS requests FROM logs_detail WHERE event_date = today() GROUP BY time ORDER BY time | 使用 toStartOf* 对齐时间轴;避免 SELECT *,只取必要字段。 |
| 使用变量(Variables) | 实现动态过滤 | 创建变量 country_list:SELECT DISTINCT country FROM user_events;在查询中使用:WHERE country = '$country_list' | 变量支持 multi-value;可创建时间范围、应用版本等变量。 |
| 高级功能 | 告警 + Annotations | 告警示例:SELECT countIf(status>=500) AS errors FROM logs_detail WHERE toStartOfMinute(event_time) = toStartOfMinute(now()) HAVING errors > 10 | 告警需配置通知渠道(如 Slack、邮件);Annotations 用于关联日志与发布。 |
| 性能优化 | 避免 Grafana 成为性能瓶颈 | 启用缓存(Grafana 内置或 Redis);对高频查询建立物化视图;限制查询时间范围 | 大屏轮询可能产生大量查询,建议聚合后查询。 |
最佳实践:
- 为 Grafana 用户授予最小必要权限(如仅 SELECT 到特定表)。
- 使用 Prometheus +
clickhouse-exporter监控 ClickHouse 自身状态,与业务指标同屏展示。- 复杂查询使用预聚合表,避免实时计算。
13.4 高并发写入场景优化
| 优化维度 | 策略 | 具体实施 | 原理说明 |
|---|---|---|---|
| 写入方式 | 批量写入,避免单条 INSERT | 客户端累积 1000~10000 条后批量提交;使用 INSERT INTO ... FORMAT JSONEachRow 或 Native | 减少网络往返和事务开销;单次写入越大,吞吐越高。 |
| 表引擎选择 | 使用 ReplicatedReplacingMergeTree 或 SummingMergeTree | ENGINE = ReplicatedReplacingMergeTree(...) ORDER BY (key, ts) | Replacing 按主键去重;Summing 自动合并指标。 |
| 分区与排序键 | 合理设计减少合并压力 | 分区粒度:天或小时(避免太细);排序键:高频查询字段前缀 | 大分区减少 parts 数量;好的排序键提升查询和合并效率。 |
| 后台合并控制 | 调整合并参数 | ALTER TABLE t MODIFY SETTING max_bytes_to_merge_at_max_space_in_pool = 134217728, number_of_free_entries_in_pool_to_lower_max_size_of_merge = 8, background_pool_size = 16; | 避免大合并阻塞查询线程池;增加后台线程数提升合并吞吐。 |
| 内存与线程 | 增加写入缓冲 | ALTER TABLE t MODIFY SETTING min_insert_block_size_rows = 10000, max_insert_block_size = 1048576; SET max_insert_threads = 4; | min_insert_block_size_rows 触发本地排序;max_insert_threads 并行处理。 |
| 分布式写入 | 写入 Distributed 表 | CREATE TABLE dist_events ON CLUSTER cl ENGINE = Distributed(cl, db, local_events, rand()); | 客户端只需连接一个节点;自动路由到分片。 |
| 限流与降级 | 应用层控制 | 使用消息队列(Kafka)缓冲;写入失败时本地缓存重试 | 防止雪崩,保证最终一致性。 |
最佳实践:
- 写入吞吐目标:单节点可达 50~100 MB/s(SSD)。
- 避免频繁小批量写入(如每秒 1 次),建议每秒 1~10 批。
- 监控
system.metrics中 AsyncInsertThreads 和 Merge 相关指标。
13.5 大数据量下的归档与冷热分离
| 策略 | 说明 | 实施方法 | 优点 | 缺点 |
|---|---|---|---|---|
| TTL 自动归档 | 数据过期后自动删除或移动 | 创建表时定义 TTL + 存储策略配置(fast ssd, slow hdd) | 自动化管理生命周期;热数据在 SSD,冷数据在 HDD | 移动是异步的,有延迟;不支持外部存储(如 S3)直接访问。 |
| S3 冷存储(S3 Disk) | 将旧分区直接存入 S3 | 配置 S3 为磁盘(disk_s3),在 TTL 中使用 TO DISK 's3' | 成本极低(S3 价格);无限扩展 | 查询冷数据较慢;需网络访问 S3。 |
| 分层表设计 | 热表 + 冷表 + 物化视图 | sales_hot(最近 7 天,SSD)+ sales_cold(历史数据,S3/HDD)+ mv_sales_to_cold(每日迁移) | 完全控制数据流动;可定制归档逻辑 | 需维护数据移动脚本;查询需 UNION ALL。 |
| 外部系统归档 | 导出到 Hadoop、对象存储 | 使用 clickhouse-backup 备份到 S3 或 INSERT INTO S3(...) 导出 | 解耦分析系统;长期保存 | 无法直接查询归档数据;需额外工具恢复。 |
| 查询路由 | 应用层判断查询范围 | Python:if query_date > today() - 7: table = "sales_hot" else: table = "sales_cold" | 精准控制性能;避免扫描冷数据 | 增加应用复杂度;跨冷热查询困难。 |
最佳实践:
- 推荐组合:TTL + S3 Disk 实现自动化冷热分离。
- 监控
system.parts中disk_name,确认数据已迁移。- 对冷数据查询,可接受 2~5 秒延迟,避免影响热查询。
总结:
- 实时日志:Kafka + 物化视图 + TTL 是标准架构。
- 用户行为:宽表 + Enum +
windowFunnel支持复杂分析。- Grafana:专用用户 + 预聚合 + 变量实现高效可视化。
- 高并发写入:批量 + 合理表引擎 + 后台合并调优。
- 冷热分离:
TTL TO DISK 's3'是低成本、可扩展的首选方案。