ClickHouse 创建数据库建表视图字典 SQL

79070248b9156c66230ce8d5c436c28f.png

1.1. ClickHouseSQL之****数据定义语言 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            │
└─────────────────────┘

如果想要看当前ClickHouseServer 进程实例下更加详细的数据库列表信息,可以使用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 BYexpr,指定排序字段元组,也即索引列(ClickHouse中的索引列即排序列)。从左到右顺序,建立稀疏索引组合键。合理设计排序键,对查询性能会有很大提升。一般选择查询条件中,筛选条件频繁的列。可以是单一维度,也可以是组合维度的索引。另外,基数特别大的不适合做索引列,如用户标签表的user_id字段。通常筛选后的数据量在百万级别,性能表现较好。

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

13.SAMPLE BYexpr,指定采样字段。

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 7DAY,其中 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
(
    UserID     UInt64,
    WatchID    UInt64,
    EventTime  DateTime,
    Sex        UInt8,
    Age        UInt8,
    OS         UInt8,
    RegionID   UInt32,
    RequestNum UInt32,
    EventDate  Date,
    PROJECTION pOS(
        SELECT
        groupBitmap(UserID),
        count(1)
        GROUP BY OS
        ),
    PROJECTION pRegionID(
        SELECT count(1),
        groupBitmap(UserID)
        GROUP BY RegionID
        )
) engine = MergeTree()
      order by (WatchID, UserID, EventTime)
      partition by (EventDate);
ALTER TABLE****添加PROJECTION

也可以通过alter table 语句添加PROJECTION 定义:

ALTER TABLE clickhouse_tutorial.user_tag
ADD PROJECTION pRegionID(SELECT count(1),groupBitmap(UserID) GROUP BY RegionID);

插入数据:

INSERT INTO clickhouse_tutorial.user_tag
(UserID,
 WatchID,
 EventTime,
 Sex,
 Age,
 OS,
 RegionID,
 RequestNum,
 EventDate)
select UserID,
       WatchID,
       EventTime,
       Sex,
       Age,
       OS,
       RegionID,
       RequestNum,
       EventDate
from tutorial.hits_v1;

可以看到 ClickHouse 数据库表文件目录下面多了两个文件夹 pOS.proj和 pRegionID.proj:

.
├── checksums.txt
├── columns.txt
├── count.txt
├── data.bin
├── data.mrk3
├── default_compression_codec.txt
├── minmax_EventDate.idx
├── pOS.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── pRegionID.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── partition.dat
└── primary.idx
 
2 directories, 23 files

每个proj文件夹下面的数据文件有checksums.txt、columns.txt、count.txt、data.bin、data.mrk3、default_compression_codec.txt、primary.idx等(Projection Part复用表分区信息),跟一张表的数据文件基本一样(可以看出,Projection本身就是表)。其中,pOS.proj/columns.txt文件内容是:

columns format version: 1
2 columns:
`OS` UInt8
`groupBitmap(UserID)` AggregateFunction(groupBitmap, UInt64)

pRegionID.proj/columns.txt文件内容是:

columns format version: 1
2 columns:
`RegionID` UInt32
`groupBitmap(UserID)` AggregateFunction(groupBitmap, UInt64)
手动触发物化PROJECTION

注意,只有在创建 PROJECTION 之后,再被写入的数据,才会自动物化。对于历史数据,需要手动触发物化,执行如下 SQL:

alter table clickhouse_tutorial.user_tag MATERIALIZE PROJECTION pOS;
alter table clickhouse_tutorial.user_tag MATERIALIZE PROJECTION pRegionID;

MATERIALIZE PROJECTION 是一个异步的 Mutation 操作,可以通过下面的语句查询状态:

SELECT
    table,
    mutation_id,
    command,
    is_done
FROM system.mutations
 
Query id: b50679e1-963f-4800-9366-abc08539ca23
 
┌─table────┬─mutation_id──────┬─command──────────────────────────┬─is_done─┐
│ user_tag │ mutation_127.txt │ MATERIALIZE PROJECTION pOS       │       1 │
│ user_tag │ mutation_128.txt │ MATERIALIZE PROJECTION pRegionID │       1 │
└──────────┴──────────────────┴──────────────────────────────────┴─────────┘
 
2 rows in set. Elapsed: 0.004 sec.

等MATERIALIZE PROJECTION 生成好了之后,分区目录如下:

.
├── Age.bin
├── Age.mrk2
├── EventDate.bin
├── EventDate.mrk2
├── EventTime.bin
├── EventTime.mrk2
├── OS.bin
├── OS.mrk2
├── RegionID.bin
├── RegionID.mrk2
├── RequestNum.bin
├── RequestNum.mrk2
├── Sex.bin
├── Sex.mrk2
├── UserID.bin
├── UserID.mrk2
├── WatchID.bin
├── WatchID.mrk2
├── checksums.txt
├── columns.txt
├── count.txt
├── default_compression_codec.txt
├── minmax_EventDate.idx
├── pOS.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── pRegionID.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── partition.dat
└── primary.idx
 
2 directories, 39 files

看到在原有 MergeTree 的分区下,多了两个pOS.proj和pRegionID.proj子目录,文件存储和 MergeTree 存储格式是一样的,其中,Projection Part复用表分区信息(minmax_EventDate.idx、partition.dat)。Projection写入与原始数据写入过程一致。每一份数据part写入都会基于原始数据Block,结合Projection定义,计算好聚合数据,然后写入对应分区Part文件。

当查询命中某个 PROJECTION 的时候,就会直接用proj子目录中的数据来加速查询。

设置投影优化生效参数

有了PROJECTION 之后,想要查询时投影优化生效,需要设置allow_experimental_projection_optimization参数开启这项功能:

SET allow_experimental_projection_optimization=1;

查看SQL执行计划:

EXPLAIN actions = 1
SELECT
    RegionID,
    count(1)
FROM clickhouse_tutorial.user_tag
GROUP BY RegionID
 
Query id: d8a96bb5-c7d5-48bd-98a6-dd148618ef5d
 
┌─explain───────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY))                               │
│ Actions: INPUT :: 0 -> RegionID UInt32 : 0                                │
│          INPUT :: 1 -> count() UInt64 : 1                                 │
│ Positions: 0 1                                                            │
│   SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│     ReadFromStorage (MergeTree(with Aggregate projection pRegionID))      │
└───────────────────────────────────────────────────────────────────────────┘
 
6 rows in set. Elapsed: 0.003 sec.

其中,ReadFromStorage (MergeTree(with Aggregate projection pRegionID)) 表示查询命中 Projection。

如果关闭投影优化,设置如下:

SET allow_experimental_projection_optimization=0;

再次查看 SQL 执行计划,可以看到&

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值