ClickHouse支持一种基于SQL的声明式查询语言,它在许多情况下与ANSI SQL标准相同。

支持的查询GROUP BY, ORDER BY, FROM, JOIN, IN以及非相关子查询。

创建数据库

ClickHouse默认使用default数据库,默认使用Atomic数据库引擎

创建数据库SQL语法

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

ON CLUSTER 指定集群名称,指定后会在集群所有节点上创建数据库

执行 show databases 查看节点所有数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
28b9c4cb4ed6 :) show databases;

SHOW DATABASES

Query id: 627e2dc7-8f4a-4ebd-a329-508a6205dc79

┌─name───────────────┐
 INFORMATION_SCHEMA 
 default            
 information_schema 
 system             
└────────────────────┘

4 rows in set. Elapsed: 0.004 sec

创建表

创建表SQL语法

1
2
3
4
5
6
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [NULL|NOT NULL] [DEFAULT|MATERIALIZED|EPHEMERAL|ALIAS expr1] [compression_codec] [TTL expr1],
    name2 [type2] [NULL|NOT NULL] [DEFAULT|MATERIALIZED|EPHEMERAL|ALIAS expr2] [compression_codec] [TTL expr2],
    ...
) ENGINE = engine

ON CLUSTER: 指定集群,在集群上创建表

创建一个MergeTree引擎的表test, 用创建日期create_time字段按天分区。

1
2
3
4
5
6
7
8
9
create table if not exists test (
   id UInt32 COMMENT 'id',
   shop_id String COMMENT '',
   amount Decimal(16,2) COMMENT '',
   create_time Datetime COMMENT '创建时间'
) engine =MergeTree()
partition by toYYYYMMDD(create_time) 
primary key (id)
order by (id, shop_id);

order by(必选)

`order by` 设定了分区内的数据按照哪些字段顺序进行有序保存。

`order by` 是 `MergeTree` 中唯一一个必填项,甚至比 primary key 还重要,因为当不设置主键的情况,很多处理会依照 order by 的字段进行处理

要求:主键必须是 `order by` 字段的前缀字段。

比如 order by 字段是 (id, shop_id) 那么主键必须是 id 或者(id, shop_id)

primary key 主键(可选)

ClickHouse 中的主键,和其他数据库不太一样,它只提供了数据的一级索引,
但是却不是唯一约束。这就意味着是可以存在相同 primary key 的数据的。

主键的设定主要依据是查询语句中的 where 条件。

根据条件通过对主键进行某种形式的二分查找,能够定位到对应的index granularity,避免了全表扫描。

index granularity: 直接翻译的话就是索引粒度,指在稀疏索引中两个相邻索引对应数据的
间隔。ClickHouse 中的 MergeTree 默认是 8192。官方不建议修改这个值,除非该列存在
大量重复值,比如在一个分区中几万行才有一个不同数据。

artition by 分区(可选)

1)作用:分区的目的主要是降低扫描的范围,优化查询速度。如果不指明partition by的话,只会使用一个分区

2)分区目录:MergeTree 是以列文件+索引文件+表定义文件组成的,
但是如果设定了分区那么这些文件就会保存到不同的分区目录中。

3)并行:分区后,面对涉及跨分区的查询统计,ClickHouse 会以分区为单位并行处理。

4)数据写入与分区合并:任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。
写入后的某个时刻(大概 10-15 分钟后),ClickHouse 会自动执行合并操作(等不及也可以
手动。通过 optimize 执行),把临时分区的数据,合并到已有分区中。

手动合并执行SQL `optimize table test final;`

执行 show tables 查看当前库中所有表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
28b9c4cb4ed6 :) show tables;

SHOW TABLES

Query id: a2e44d1e-6617-426a-9600-0d7df0a768fa

┌─name─┐
 test 
└──────┘

1 rows in set. Elapsed: 0.005 sec.

使用 describe test 查看表字段属性

分批插入数据

1
2
3
4
5
6
7
insert into test(id, shop_id, amount, create_time) VALUES 
(1, '101', '11.00', toDateTime('2023-01-01 00:00:00')),
(2, '102', '12.00', toDateTime('2023-01-01 00:00:00'));

insert into test(id, shop_id, amount, create_time) VALUES 
(3, '103', '13.00', toDateTime('2023-01-01 00:00:00')),
(4, '104', '14.00', toDateTime('2023-01-01 00:00:00'));

查询数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
28b9c4cb4ed6 :) select * from test;

SELECT *
FROM test

Query id: 989e4853-0eea-46fe-ba2f-81816580839e

┌─id─┬─shop_id─┬─amount─┬─────────create_time─┐
  3  103          13  2023-01-01 00:00:00 
  4  104          14  2023-01-01 00:00:00 
└────┴─────────┴────────┴─────────────────────┘
┌─id─┬─shop_id─┬─amount─┬─────────create_time─┐
  1  101          11  2023-01-01 00:00:00 
  2  102          12  2023-01-01 00:00:00 
└────┴─────────┴────────┴─────────────────────┘

4 rows in set. Elapsed: 0.006 sec.

分批插入的数据会生成新的临时片段,存储底层对应新的分区文件,可以通过system.parts表查看。

执行如下SQL查看test表的分区情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
28b9c4cb4ed6 :)  SELECT
                 database,
                 table,
                 partition,
                 partition_id,
                 name,
                 active,
                 path
                 FROM system.parts
                 WHERE table = 'test'

SELECT
    database,
    table,
    partition,
    partition_id,
    name,
    active,
    path
FROM system.parts
WHERE table = 'test'

Query id: 55ddd330-e30c-4918-8777-bfa9c3b56dc2

┌─database─┬─table─┬─partition─┬─partition_id─┬─name───────────┬─active─┬─path───────────────────────────────────────────────────────────────────────────────┐
 default   test   20230101   20230101      20230101_1_1_0       1  /var/lib/clickhouse/store/683/6838f6bf-7c41-4b02-a838-f6bf7c417b02/20230101_1_1_0/ 
 default   test   20230101   20230101      20230101_2_2_0       1  /var/lib/clickhouse/store/683/6838f6bf-7c41-4b02-a838-f6bf7c417b02/20230101_2_2_0/ 
└──────────┴───────┴───────────┴──────────────┴────────────────┴────────┴────────────────────────────────────────────────────────────────────────────────────┘

2 rows in set. Elapsed: 0.006 sec.

MergeTree引擎会在插入数据15分钟左右,将同一个分区的各个分区文件片段合并成一整个分区文件。

也可以手动执行 OPTIMIZE 语句手动触发合并指定的分区片段。

1
OPTIMIZE TABLE test PARTITION '20230101';

合并分区操作

合并分区操作SQL语法

1
OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
  • OPTIMIZE TABLE test PARTITION ‘partition’:合并指定分区(partition),如果有多个分区,需要执行多次。
  • OPTIMIZE TABLE test:合并一个分区,如果有多个分区,需要执行多次。
  • OPTIMIZE TABLE test FINAL:合并所有分区。

参考