【ClickHouse SQL 极简教程】ClickHouse SQL之数据定义语言 DDL_java

1.1. ClickHouse SQL之数据定义语言 DDL 

本节介绍 ClickHouse 中进行数据库、表结构的定义和管理。

1.1.1. 概述

在SQL中,数据定义语言( DDL ) 用来创建和修改数据库Schema,例如表、索引和用户等。其中数据库的Schema描述了用户数据模型、字段和数据类型。DDL 语句类似于用于定义数据结构的计算机编程语言。常见DDL 语句包括CREATE、ALTER、DROP等。

ClickHouse SQL中的DDL,除了可以定义数据库、表、索引和视图等之外,还可以定义函数和字典等。

1.1.2. 创建数据库

语法

CREATE DATABASE [IF NOT EXISTS] db_name

[ON CLUSTER cluster]

[ENGINE = db_engine(...)]

[COMMENT 'Comment']

功能说明

1. 创建名称为db_name的数据库。

2. 如果指定了 ON CLUSTER cluster 子句,那么在指定集群 cluster 的所有服务器上创建 db_name 数据库。

3. ENGINE = db_engine(...), 数据库引擎。ClickHouse 默认使用 Atomic 数据库引擎,即有默认值 ENGINE = Atomic。Atomic 引擎提供了可配置的 table engines 和 SQL dialect,它支持非阻塞的DROP TABLE和RENAME TABLE查询和原子的表交换查询命令 EXCHANGE TABLES t1 AND t2。Atomic 中的所有表都有持久的 UUID,数据存储在/clickhouse_path/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/ 路径下。其中,xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy是表 UUID,支持在不更改 UUID 和移动表数据的情况下执行重命名。可以使用DatabaseCatalog,通过 UUID访问 Atomic 数据库中的表。执行DROP TABLE 命令,不会删除任何数据,Atomic 数据库只是通过将元数据移动到 /clickhouse_path/metadata_dropped/,并将表标记为已删除,并通知 DatabaseCatalog。

在 20.5 版本中(可以使用 SELECT version() 查看 ClickHouse 版本),ClickHouse 首次引入了数据库引擎 Atomic。从 20.10 版开始,它是默认数据库引擎(之前默认使用 engine=Ordinary )。SQL 如下:

SELECT version()

Query id: 45ca90b4-5e17-4106-a92f-f8fed8822286

┌─version()────┐

│ 21.12.1.8808 │

└──────────────┘

SHOW CREATE DATABASE system

FORMAT TSVRaw

Query id: 19d3e06d-dfaf-45b4-a67d-156199098bc4

CREATE DATABASE system

ENGINE = Atomic

这两个数据库引擎在文件系统上存储数据的方式有所不同(Ordinary引擎的文件系统布局更简单),Atomic 引擎解决了Ordinary引擎中存在的一些问题。

Atomic 引擎支持非阻塞删除表/重命名表、异步表删除和分离(delete&detach)(等待查询完成,但对新查询不可见)、原子删除表(删除所有文件/文件夹)、原子表交换(通过“EXCHANGE TABLES t1 AND t2”进行表交换)、重命名字典/重命名数据库、

在FS和ZK 中自动生成唯一UUID 路径用于复制。ClickHouse 支持的数据库引擎可以在 源码目录 src/Databases 下面找到。例如在DatabaseFactory.cpp(112行)中,可以看到 ClickHouse数据库引擎集合:

database_engines {"Ordinary", "Atomic", "Memory","Dictionary", "Lazy", "Replicated", "MySQL", "MaterializeMySQL", "MaterializedMySQL", "PostgreSQL", "MaterializedPostgreSQL","SQLite"}

4. COMMENT 'Comment',添加数据库注释。所有数据库引擎都支持该注释。

Ø 数据库引擎Atomic与Ordinary对比

数据库引擎

Ordinary

Atomic

文件系统布局

简单

复杂

外部工具支持,如 clickhouse-backup等

好/成熟

有限/测试版

一些 DDL 查询(DROP / RENAME)可能要挂起等待很长时间

是的

没有

支持交换 2 张表操作

renamea to a_old,b to a,a_old to b;操作不是原子的,并且 可以在中间突破(虽然机会很低)。

EXCHANGE TABLES t1 AND t2;原子操作,没有中间状态。

使用 zookeeper路径中的 uuid

无法使用。典型的模式是需要创建同一张表的新版本的时候,给zookeeper路径加上版本后缀。

可以在 zookeeper 路径中使用 uuid。可以在 zookeeper 路径中使用 uuid。但是,当扩展集群时,这需要格外小心。Zookeeper 路径更难以映射到真实表。允许对表进行任何类型的操作(重命名、使用相同名称重新创建等)。

物化视图,建议始终使用 TO 语法。

.inner.mv_name, 名字可读。

.inner_id.{uuid},名字不可读。

创建一个使用 Atomic引擎的数据库

CREATE DATABASE if not exists clickhouse_tutorial ENGINE = Atomic;

查看数据库列表

使用命令show databases 可以看到数据库列表如下。

SHOW DATABASES

Query id: 08c13dfb-0f5c-4aea-815a-68ec95eaa037

┌─name────────────────┐

│ INFORMATION_SCHEMA  │

│ clickhouse_tutorial │

│ default             │

│ information_schema  │

│ mydb                │

│ system              │

│ tutorial            │

└─────────────────────┘

如果想要看当前ClickHouse Server 进程实例下更加详细的数据库列表信息,可以使用 select * from system.databases 命令。为了可读性,输出结果整理成表格如下。

name

engine

data_path

metadata_path

uuid

comment

database

INFORMATION_SCHEMA

Memory

./


00000000-0000-0000-0000-000000000000



clickhouse_tutorial

Atomic

./store/

/Users/chenguangjian/store/3c0/3c0b76c0-1dac-4f88-bc0b-76c01dac3f88/

3c0b76c0-1dac-4f88-bc0b-76c01dac3f88



default

Atomic

./store/

/Users/chenguangjian/store/d62/d62015e0-b943-4090-9620-15e0b9432090/

d62015e0-b943-4090-9620-15e0b9432090



information_schema

Memory

./


00000000-0000-0000-0000-000000000000



mydb

Ordinary

./data/mydb/

/Users/chenguangjian/metadata/mydb/

00000000-0000-0000-0000-000000000000

mydb


system

Atomic

./store/

/Users/chenguangjian/store/268/2682f921-c33f-4278-a682-f921c33f9278/

2682f921-c33f-4278-a682-f921c33f9278



tutorial

Atomic

./store/

/Users/chenguangjian/store/d34/d34824fa-e714-43e8-9348-24fae71403e8/

d34824fa-e714-43e8-9348-24fae71403e8



可以看出,使用Atomic引擎的元数据路径上都带有 UUID,而使用Ordinary引擎的路径使用的是数据库名字,Memory引擎则没有元数据存储到磁盘文件上。

可以使用命令show create database system 查看system 数据库建库命令:

CREATE DATABASE system

ENGINE = Atomic

可见,system使用的是Atomic引擎。

1.1.3. 删除数据库

使用 DROP命令删除数据库,SQL实例如下:

drop DATABASE if exists clickhouse_tutorial;

使用 select * from system.databases 查看数据库列表里面,clickhouse_tutorial已经被删掉。

1.1.4. 创建MergeTree表 

语法

ClickHouse 中最强大的表引擎是*MergeTree家族系列,也是使用最多的表引擎。

创建MergeTree表SQL语法如下:

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name]

(

    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],

    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],

    ...

    INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,

    INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2,

    ...

    PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),

    PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])

) ENGINE = MergeTree()

ORDER BY expr

[PARTITION BY expr]

[PRIMARY KEY expr]

[SAMPLE BY expr]

[TTL expr

    [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]

    [WHERE conditions]

    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]

[SETTINGS name=value, ...]

功能说明

1. ON CLUSTER cluster_name,在ClickHouse集群cluster_name上建表。默认情况下,表仅在当前服务器上创建。如果是在ClickHouse集群cluster_name的所有服务节点上建表,使用分布式 DDL 查询子句 ON CLUSTER 实现。

2. ENGINE = table_engine,指定表引擎。可以通过SQL查询system.table_engines表内容,获得ClickHouse支持的表引擎。如下所示。

SELECT

    name,

    supports_ttl

FROM system.table_engines

Query id: f219cd4d-dd2b-4490-a9aa-e66f29cec9ac

┌─name───────────────────────────────────┬─supports_ttl─┐

│ PostgreSQL                             │            0 │

│ RabbitMQ                               │            0 │

│ Kafka                                  │            0 │

│ S3                                     │            0 │

│ ExecutablePool                         │            0 │

│ MaterializedView                       │            0 │

│ MaterializedPostgreSQL                 │            0 │

│ EmbeddedRocksDB                        │            0 │

│ View                                   │            0 │

│ JDBC                                   │            0 │

│ Join                                   │            0 │

│ ExternalDistributed                    │            0 │

│ Executable                             │            0 │

│ Set                                    │            0 │

│ Dictionary                             │            0 │

│ GenerateRandom                         │            0 │

│ LiveView                               │            0 │

│ MergeTree                              │            1 │

│ Memory                                 │            0 │

│ Buffer                                 │            0 │

│ MongoDB                                │            0 │

│ URL                                    │            0 │

│ ReplicatedVersionedCollapsingMergeTree │            1 │

│ ReplacingMergeTree                     │            1 │

│ ReplicatedSummingMergeTree             │            1 │

│ COSN                                   │            0 │

│ ReplicatedAggregatingMergeTree         │            1 │

│ ReplicatedCollapsingMergeTree          │            1 │

│ File                                   │            0 │

│ ReplicatedGraphiteMergeTree            │            1 │

│ ReplicatedMergeTree                    │            1 │

│ ReplicatedReplacingMergeTree           │            1 │

│ VersionedCollapsingMergeTree           │            1 │

│ SummingMergeTree                       │            1 │

│ Distributed                            │            0 │

│ TinyLog                                │            0 │

│ GraphiteMergeTree                      │            1 │

│ SQLite                                 │            0 │

│ CollapsingMergeTree                    │            1 │

│ Merge                                  │            0 │

│ AggregatingMergeTree                   │            1 │

│ ODBC                                   │            0 │

│ Null                                   │            0 │

│ StripeLog                              │            0 │

│ Log                                    │            0 │

└────────────────────────────────────────┴──────────────┘

45 rows in set. Elapsed: 0.001 sec.

3. (name1 [type1] , ... ),指定表字段名和字段数据类型。

4. NULL|NOT NULL,字段是否可空。

5. DEFAULT,指定字段默认值。字段可以通过表达式 DEFAULT expr指定默认值。如果 INSERT 查询没有指定对应的列,则通过计算对应的表达式来填充。例如:`URLDomain String DEFAULT domain(URL)`。如果未定义默认值的表达式,则默认值将设置为数字为零,字符串为空字符串,数组为空数组,日期为 1970-01-01,DateTime unix时间戳为零,NULL为空。

6. MATERIALIZED,物化字段列。这样的字段不能在 INSERT 语句中指定值插入,因为这样的字段总是通过使用其他字段计算出来的。

7. EPHEMERAL,临时字段列。这样的列不存储在表中,不能被SELECT 查询,但可以在 CREATE 语句的默认值中引用。

8. ALIAS,字段别名。该别名值不会存储在表中,SELECT 查询中使用星号时不会被替换。如果在查询解析期间扩展了别名,则可以在 SELECT 中使用它。

9. INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,指定索引字段和索引粒度。

10. PROJECTION projection_name_1,指定物化投影字段创建一个 Projection,为当前字段Where查询加速。Projection 思想源自 《C-Store: A Column-oriented DBMS》这篇论文,作者是2015年图灵奖获得者、Vertica 之父,Mike Stonebraker。Projection 意指一组列的组合,可以按照与原表不同的排序存储,并且支持聚合函数的查询。相当于传统意义上的物化视图,用空间换时间。它借鉴 MOLAP 预聚合的思想,在数据写入的时候,根据 PROJECTION定义的表达式,计算写入数据的聚合数据同原始数据一并写入。数据查询的过程中,如果查询SQL通过分析可以通过聚合数据得出,直接查询聚合数据减少计算的开销,解决了由于数据量导致的内存问题。Projection主要分为两种:normal与aggregate。相关源码参阅MergeTreeDataWriter.cpp(574 行)MergeTreeDataWriter::TemporaryPart MergeTreeDataWriter::writeProjectionPart()。

11. ORDER BY expr,指定排序字段元组,也即索引列(ClickHouse中的索引列即排序列)。从左到右顺序,建立稀疏索引组合键。合理设计排序键,对查询性能会有很大提升。一般选择查询条件中,筛选条件频繁的列。可以是单一维度,也可以是组合维度的索引。另外,基数特别大的不适合做索引列,如用户标签表的user_id字段。通常筛选后的数据量在百万级别,性能表现较好。

12. PARTITION BY expr,指定分区字段元组。分区字段数据存储到独立的文件夹目录下。

13. SAMPLE BY expr,指定采样字段。

14. compression_codec,指定压缩算法。通用编解码器有NONE(无压缩)、LZ4、LZ4HC、ZSTD。ClickHouse 默认使用 LZ4压缩算法CODEC(LZ4)。专用编解码器有Delta(delta_bytes)、DoubleDelta(适用于时间序列数据)、Gorilla、T64等。另外,使用CODEC('AES-128-GCM-SIV')、CODEC('AES-256-GCM-SIV')等可以加密磁盘上的数据。

15. TTL expr,表数据存活时间表达式。到期数据ClickHouse 会自动清理,对于数据 Part移动或重新压缩,数据的所有行都必须满足“TTL”表达式条件。只能为 MergeTree 系列表指定。TTL 子句不能用于主键列。例如,根据 date字段判断哪些数据到期, TTL表达式为:TTL date + INTERVAL 7 DAY,其中 date 为日期字段。那么,date=20220301的数据将会在 7 天后,也就是 20220308 零点日期被清理。

16. PRIMARY KEY,索引主键。

17. SETTINGS name=value, ...,指定配置项name=value,多个配置项之间用逗号分隔。例如,指定表的索引粒度:SETTINGS index_granularity = 8192。

实例讲解

创建MergeTree表

create table if not exists clickhouse_tutorial.user_tag

(

    user_id      UInt64 DEFAULT 0,

    gender       String DEFAULT 'NA',

    age          String DEFAULT 'NA',

    active_level String DEFAULT 'NA',

    date         Date

) engine = MergeTree()

      order by (user_id, active_level)

      primary key (user_id)

      partition by (date);

使用 show create table clickhouse_tutorial.user_tag 命令查询建表 SQL,执行结果如下:

SHOW CREATE TABLE clickhouse_tutorial.user_tag

FORMAT TSVRaw

Query id: 30ec8e45-8ead-419e-b8fc-dbce54b5939c

CREATE TABLE clickhouse_tutorial.user_tag

(

    `user_id` UInt64 DEFAULT 0,

    `gender` String DEFAULT 'NA',

    `age` String DEFAULT 'NA',

    `active_level` String DEFAULT 'NA',

    `date` Date

)

ENGINE = MergeTree

PARTITION BY date

PRIMARY KEY user_id

ORDER BY (user_id, active_level)

SETTINGS index_granularity = 8192

1 rows in set. Elapsed: 0.001 sec.

其中,index_granularity = 8192是默认的稀疏索引间隔行数。

使用PROJECTION创建MergeTree 表

使用PROJECTION语句建表:

create table if not exists clickhouse_tutorial.user_tag