4. 数据库与表操作:从建库到删表,一次讲透

好,咱们进入正题。数据库和表,是 ClickHouse 里最基础的操作。说白了,你所有的数据都得先有个“仓库”放进去,这个仓库就是数据库;而表,就是仓库里一个个的货架。

我个人习惯,拿到一个新环境,第一件事就是先建库。别嫌麻烦,后面管理起来会省心很多。

4.1 创建数据库

建库的语法很简单,但有几个细节我得跟你聊聊。

CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster_name] [ENGINE = engine(...)];

这里最常用的引擎是 Atomic,也是默认的。它支持原子性的表创建和删除,不会出现“表删了一半,数据丢了”这种尴尬情况。

小提示: 如果你用的是 ClickHouse 21.1 之后的版本,默认引擎就是 Atomic。老版本可能是 Ordinary,但我不建议你用 Ordinary,因为它不支持原子操作。

举个例子:

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 会默认用排序键作为主键。

重点: MergeTree 的主键不是唯一约束。它只是一个索引,允许重复值。这一点和 MySQL 完全不同,千万别搞混了。

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 测试一下数据是否能转换。

警告: 修改列类型时,ClickHouse 不会自动转换已有数据。它只会改变元数据。如果你需要转换历史数据,得手动写 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 本章知识体系图

下面这张图,帮你把本章的核心操作串起来。

数据库与表操作知识体系 创建数据库 创建表 (MergeTree) 查看表结构 修改表 删除表 核心要点: • 排序键 (ORDER BY) 决定查询性能,把最常用的过滤字段放前面 • 分区键 (PARTITION BY) 减少扫描量,按时间分区是最常见的做法 • 删除表前务必确认,最好先改名备份

4.7 避坑指南

最后,分享几个我踩过的坑。

  • 分区键别乱改: 表建好后,分区键就不能改了。除非你重建表。所以建表前一定要想清楚。
  • 排序键也别乱改: 虽然 ClickHouse 支持修改排序键,但代价很大——它会重写所有数据。数据量大的时候,可能跑几个小时。
  • 删除列要小心: 我见过有人想删一列,结果写错了列名,把另一列删了。还好有备份,不然就凉了。
  • 别在生产环境直接 DROP: 哪怕你 100% 确定,也建议先 RENAME 到备份库,观察几天再删。