4. 数据库与表操作:从建库到删表,一次讲透
好,咱们进入正题。数据库和表,是 ClickHouse 里最基础的操作。说白了,你所有的数据都得先有个“仓库”放进去,这个仓库就是数据库;而表,就是仓库里一个个的货架。
我个人习惯,拿到一个新环境,第一件事就是先建库。别嫌麻烦,后面管理起来会省心很多。
4.1 创建数据库
建库的语法很简单,但有几个细节我得跟你聊聊。
CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster_name] [ENGINE = engine(...)];
这里最常用的引擎是 Atomic,也是默认的。它支持原子性的表创建和删除,不会出现“表删了一半,数据丢了”这种尴尬情况。
举个例子:
CREATE DATABASE IF NOT EXISTS my_analytics;
这条命令会创建一个叫 my_analytics 的库。如果它已经存在,也不会报错。
嗯,这里要注意:如果你在集群环境里,想在所有节点上都创建同一个库,可以加上 ON CLUSTER 子句。比如:
CREATE DATABASE IF NOT EXISTS my_analytics ON CLUSTER my_cluster;
我在项目中遇到过,有人忘了加 IF NOT EXISTS,结果重复执行脚本时直接报错。虽然不是什么大问题,但挺影响心情的。
4.2 创建表:MergeTree 引擎
表是 ClickHouse 的核心。而 MergeTree 家族,是 ClickHouse 最强大的引擎系列。你想想看,如果没有 MergeTree,ClickHouse 的查询速度根本不可能这么快。
先看一个最基础的建表语句:
CREATE TABLE my_analytics.events
(
event_date Date,
event_time DateTime,
user_id UInt32,
event_type String,
event_value Float64
)
ENGINE = MergeTree()
ORDER BY (event_date, event_time)
PARTITION BY toYYYYMM(event_date);
这里有几个关键点,我一个个说。
4.2.1 排序键(ORDER BY)
这是 MergeTree 的灵魂。排序键决定了数据在磁盘上的物理存储顺序。查询时,如果条件匹配排序键的前缀,ClickHouse 可以做到极快的索引定位。
我个人习惯,把最常用的过滤字段放在排序键的最前面。比如上面的例子,如果大部分查询都按 event_date 和 event_time 来过滤,那这个排序键就很合理。
4.2.2 分区键(PARTITION BY)
分区不是必须的,但强烈建议加。分区可以把数据按时间或其他维度切分成多个小文件。查询时,如果只涉及某几个分区,ClickHouse 会直接跳过不相关的分区,大幅减少扫描量。
我曾经见过一个案例,有人没设分区键,结果一张表里存了 3 年的数据,每次查询都要扫全表。加了按月分区后,查询速度提升了 10 倍不止。
4.2.3 主键(PRIMARY KEY)
在 MergeTree 里,主键和排序键可以不一样。但说实话,大多数情况下它们是一样的。如果你不显式指定主键,ClickHouse 会默认用排序键作为主键。
4.3 查看表结构
建完表后,怎么确认它长什么样?有几种方式。
4.3.1 DESCRIBE TABLE
最直接的方式:
DESCRIBE TABLE my_analytics.events;
它会返回每一列的字段名、类型、默认值等信息。简单粗暴。
4.3.2 SHOW CREATE TABLE
如果你想看完整的建表语句,包括引擎参数、分区设置等,用这个:
SHOW CREATE TABLE my_analytics.events;
这个命令特别有用。比如你要在另一个环境重建一模一样的表,直接复制输出结果就行。
4.3.3 系统表查询
如果你想知道更详细的信息,比如分区大小、数据量,可以查系统表:
SELECT *
FROM system.tables
WHERE database = 'my_analytics' AND name = 'events';
嗯,这里要注意:系统表 system.tables 里有很多字段,比如 engine、partition_key、sorting_key 等,都是排查问题的好帮手。
4.4 修改表
表建好了,但需求总会变。ClickHouse 支持一些常见的表结构修改操作。
4.4.1 添加列
ALTER TABLE my_analytics.events
ADD COLUMN new_column String AFTER event_type;
这里 AFTER 指定了新列放在哪一列后面。如果不指定,默认加到最后。
4.4.2 删除列
ALTER TABLE my_analytics.events
DROP COLUMN new_column;
注意:删除列后,该列的数据会立即丢失,无法恢复。操作前最好确认一下。
4.4.3 修改列类型
ALTER TABLE my_analytics.events
MODIFY COLUMN event_value Float32;
这个操作有风险。如果新类型和旧类型不兼容,比如从 String 改成 Int,ClickHouse 会报错。我建议你先用 SELECT 测试一下数据是否能转换。
ALTER TABLE ... UPDATE 语句。
4.4.4 修改表名
RENAME TABLE my_analytics.events TO my_analytics.events_archive;
改名操作是原子的,不会影响正在进行的查询。但要注意,如果表正在被写入,改名后写入会失败。
4.5 删除表
删除表很简单,但后果很严重。
DROP TABLE my_analytics.events;
这条命令会删除表结构和所有数据。没有回收站,没有后悔药。
我曾经在测试环境里手滑删了一张生产表,还好有备份。从那以后,我每次执行 DROP TABLE 之前,都会先看一眼数据库名和表名,确认三遍。
RENAME 到一个备份库,确认没问题后再删。比如:RENAME TABLE db.table TO backup_db.table;
4.6 本章知识体系图
下面这张图,帮你把本章的核心操作串起来。
4.7 避坑指南
最后,分享几个我踩过的坑。
- 分区键别乱改: 表建好后,分区键就不能改了。除非你重建表。所以建表前一定要想清楚。
- 排序键也别乱改: 虽然 ClickHouse 支持修改排序键,但代价很大——它会重写所有数据。数据量大的时候,可能跑几个小时。
- 删除列要小心: 我见过有人想删一列,结果写错了列名,把另一列删了。还好有备份,不然就凉了。
- 别在生产环境直接 DROP: 哪怕你 100% 确定,也建议先
RENAME到备份库,观察几天再删。