Clickhouse基础语法、数据类型、数据表引擎学习

2021-03-04 10:49:59 浏览数 (1)

1、Clickhouse创建数据库的语法,如下所示:

代码语言:javascript复制
1 CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster] [ENGINE = engine(...)]

使用案例,如下所示:

代码语言:javascript复制
 1 master :) CREATE DATABASE IF NOT EXISTS gab_db;
 2 
 3 CREATE DATABASE IF NOT EXISTS gab_db
 4 
 5 Ok.
 6 
 7 0 rows in set. Elapsed: 0.013 sec. 
 8 
 9 master :) show databases;
10 
11 SHOW DATABASES
12 
13 ┌─name───────────────────────────┐
14 │ _temporary_and_external_tables │
15 │ default                        │
16 │ gab_db                         │
17 │ system                         │
18 └────────────────────────────────┘
19 
20 4 rows in set. Elapsed: 0.012 sec. 
21 
22 master :) 

2、默认情况下,ClickHouse使用的是原生的数据库引擎Ordinary(在此数据库下可以使用任意类型的表引擎,在绝大多数情况下都只需使用默认的数据库引擎)。当然也可以使用Lazy引擎和MySQL引擎,比如使用MySQL引擎,可以直接在ClickHouse中操作MySQL对应数据库中的表。假设MySQL中存在一个名为Clickhouse的数据库,可以使用下面的方式连接MySQL数据库。

代码语言:javascript复制
 1 -- --------------------------语法-----------------------------------
 2 CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
 3 ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')
 4 
 5 -- --------------------------示例------------------------------------
 6 CREATE DATABASE mysql_db ENGINE = MySQL('192.168.0.109:3306', 'clickhouse', 'root', '123456');
 7 
 8 
 9 -- ---------------------------操作-----------------------------------
10 master :) CREATE DATABASE mysql_db ENGINE = MySQL('192.168.0.109:3306', 'clickhouse', 'root', '123456');
11 
12 CREATE DATABASE mysql_db
13 ENGINE = MySQL('192.168.0.109:3306', 'clickhouse', 'root', '123456')
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.011 sec. 
18 
19 master :) show databases;
20 
21 SHOW DATABASES
22 
23 ┌─name───────────────────────────┐
24 │ _temporary_and_external_tables │
25 │ default                        │
26 │ gab_db                         │
27 │ mysql_db                       │
28 │ system                         │
29 └────────────────────────────────┘
30 
31 5 rows in set. Elapsed: 0.006 sec. 
32 
33 master :) use mysql_db;
34 
35 USE mysql_db
36 
37 Ok.
38 
39 0 rows in set. Elapsed: 0.002 sec. 
40 
41 master :) show tables;
42 
43 SHOW TABLES
44 
45 Ok.
46 
47 0 rows in set. Elapsed: 0.012 sec. 
48 
49 master :) show tables;
50 
51 SHOW TABLES
52 
53 ┌─name────┐
54 │ user_db │
55 └─────────┘
56 
57 1 rows in set. Elapsed: 0.011 sec. 
58 
59 master :) select * from user_db;
60 
61 SELECT *
62 FROM user_db
63 
64 ┌─id─┬─name─────┬─age─┬─address──────┐
65 │  1 │ zhangsan │  22 │ 河南省新乡市 │
66 └────┴──────────┴─────┴──────────────┘
67 
68 1 rows in set. Elapsed: 0.020 sec. 
69 
70 master :) 

如果创建的时候报下面的错误,是因为权限的问题,执行下面的命令,解决问题即可。

代码语言:javascript复制
 1 master :) CREATE DATABASE mysql_db ENGINE = MySQL('192.168.0.109:3306', 'clickhouse', 'root', '123456');
 2 
 3 CREATE DATABASE mysql_db
 4 ENGINE = MySQL('192.168.0.109:3306', 'clickhouse', 'root', '123456')
 5 
 6 
 7 Received exception from server (version 20.8.3):
 8 Code: 501. DB::Exception: Received from localhost:9000. DB::Exception: Cannot create MySQL database, because Poco::Exception. Code: 1000, e.code() = 1045, e.displayText() = mysqlxx::ConnectionFailed: Access denied for user 'root'@'192.168.0.109' (using password: YES) ((nullptr):3306), 
 9 
10 0 rows in set. Elapsed: 0.042 sec. 

执行下面两行命令,解决问题即可。

代码语言:javascript复制
1 grant all privileges on *.* to root@'%' identified by '123456'; 
2 
3 FLUSH PRIVILEGES;

3、Clickhouse创建数据表的,语法如下所示:

代码语言:javascript复制
1 CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
2 (
3     name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [compression_codec] [TTL expr1],
4     name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [compression_codec] [TTL expr2],
5     ...
6 ) ENGINE = engine

使用案例,如下所示:

代码语言:javascript复制
 1 master :) 
 2 master :) show tables;
 3 
 4 SHOW TABLES
 5 
 6 Ok.
 7 
 8 0 rows in set. Elapsed: 0.008 sec. 
 9 
10 master :) create table user_db(
11 :-] id Int32,
12 :-] name String
13 :-] )engine=Memory;
14 
15 CREATE TABLE user_db
16 (
17     `id` Int32,
18     `name` String
19 )
20 ENGINE = Memory
21 
22 Ok.
23 
24 0 rows in set. Elapsed: 0.003 sec. 
25 
26 master :) show tables;
27 
28 SHOW TABLES
29 
30 ┌─name────┐
31 │ user_db │
32 └─────────┘
33 
34 1 rows in set. Elapsed: 0.007 sec. 
35 
36 master :) 

上面命令是创建了一张内存表,即使用的是Memory引擎。表引擎决定了数据表的特性,也决定了数据将会被如何存储及加载。Memory引擎是ClickHouse最简单的表引擎,数据只会被保存在内存中,在服务重启时数据会丢失。

4、Clickhouse的数据类型,在创建数据表的时候指定字段的数据类型,数据类型在使用的时候是区分大小写的,所以在定义字段的时候一定注意数据类型的书写。

4.1、整数数据类型Int Ranges ,Clickhouse直接使用Int8、Int16、Int32、Int64指代4种大小的Int类型,其末尾的数据正好表明了占用字节的大小(1个节点 = 8位)。

名称

大小(字节)

范围

普遍观念

Int8

1个字节

-128到127

Tinyint

Int16

2个字节

-32768到32767

Smallint

Int32

4个字节

-2147483648到2147483647

int

Int64

8个字节

-9223372036854775808到9223372036854775807

Bigint

具体对应关系,如下所示:

代码语言:javascript复制
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 : 2147483647]
Int64 - [-9223372036854775808 : 9223372036854775807]

整数数据类型Uint Ranges,Clickhouse支持无符号的整数,使用前缀U表示,都表示的是正数,即无负数表示。

代码语言:javascript复制
UInt8 - [0 : 255]
UInt16 - [0 : 65535]
UInt32 - [0 : 4294967295]
UInt64 - [0 : 18446744073709551615]

使用案例,如下所示:

代码语言:javascript复制
 1 master :) create table tb_name(
 2 :-] id UInt8 , -- 指定数据类型
 3 :-] age UInt8 , -- 指定数据类型
 4 :-] flow Int64 -- 指定数据类型
 5 :-] ) engine=Log ; -- 指定表引擎
 6 
 7 CREATE TABLE tb_name
 8 (
 9     `id` UInt8,
10     `age` UInt8,
11     `flow` Int64
12 )
13 ENGINE = Log
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.004 sec. 
18 
19 master :) 

4.2、小数数据类型。

代码语言:javascript复制
Float32 - float,注意:和我们之前的认知是一样的,这种数据类型在数据特别精准的情况下可能出现数据精度问题。
Float64 - double
Decimal(P,S) Decimal32(s) Decimal64(s) ) Decimal128(s)。

名称

大小(字节)

有效精度(位数)

普遍概念

Float32

4个字节

7位

Float

Float64

8个字节

16位

Double

Select 8.0/0 INF表示的是正无穷,Select -8.0/0 INF表示负无穷,Select 0/0 NAN表示非数字。

使用案例,如下所示:

代码语言:javascript复制
 1 master :) create table tb_user(
 2 :-] uid Int8 ,
 3 :-] sal Decimal32(2)-- 指定2位小数点 4 :-] ) engine=TinyLog ;
 5 
 6 CREATE TABLE tb_user
 7 (
 8     `uid` Int8,
 9     `sal` Decimal32(2)
10 )
11 ENGINE = TinyLog
12 
13 Ok.
14 
15 0 rows in set. Elapsed: 0.018 sec. 
16 
17 master :) insert into tb_user values(1,10000),(2,30000),(3,2000) ;
18 
19 INSERT INTO tb_user VALUES
20 
21 Ok.
22 
23 3 rows in set. Elapsed: 0.053 sec. 
24 
25 master :) select * from tb_user;
26 
27 SELECT *
28 FROM tb_user
29 
30 ┌─uid─┬──────sal─┐
31 │   1 │ 10000.00 │
32 │   2 │ 30000.00 │
33 │   3 │  2000.00 │
34 └─────┴──────────┘
35 
36 3 rows in set. Elapsed: 0.009 sec. 
37 
38 master :) 

  Decimal如果要求高精度的数值运算,则需要使用定点数。Clickhouse提供了Decimal32、Decimal64和Decimal128三种精度的定点数。可以通过两种行式表明定点,简写方式有Decimal32(S)、Decimal64(S)、Decimal128(S)三种,原生方式为Decimal(P,S),其中,P代表了精度,决定总位数(整数部分 小数部分),取值范围是1~38,S代表规模,决定小数位,取值范围是0~P。

名称

等效声明

范围

Decimal32(S)

Decimal(1 ~ 9,S)

-1 * 10^(9-S) 到 1 * 10^(9-S)

Decimal64(S)

Decimal(10 ~ 18,S)

-1 * 10^(18-S) 到 1 * 10^(18-S)

Decimal128(S)

Decimal(19 ~ 38,S)

-1 * 10^(38-S) 到 1 * 10^(38-S)

在使用两个不同精度的定点数进行四则运算的时候,它们的小数点,位数S会发生变化。

  在进行加法运算的时候,S取最大值,例如下面的查询toDecimal64(2,4)与toDecimal32(2,2)相加后S=4;   在进行减法运算的时候,S取最大值,例如下面的查询toDecimal(2,4)与toDecimal32(2,2)相减后S=4;   在进行乘法运算的时候,S取最大值,例如下面的查询toDecimal(2,4)与toDecimal32(2,2)相乘后S=4 2;   在进行除法运算的时候,S取最大值,例如下面的查询toDecimal(2,4)与toDecimal32(2,2)相除后S=4;

但是要保证被除数的S大于除数的S,否则会报错。

4.3、boolean类型,Clickhouse中没有对布尔类型进行支持,可以使用0 和1 来代表布尔数据类型。

4.4、字符串数据类型,字符串类型可以细分为String、FixedString、UUID三类,从命名来看彷佛是由一款数据库提供的类型,反而更像一门编程语言的设计,Clickhouse语法具备编程语言的特征(数据+运算)。

  a)、String,字符串由String定义,长度不限,因此在使用String的时候无须声明大小。它完全代替了传统意义上数据库的Varchar、Text、Clob和Blob等字符类型。String类型不限定字符集,因为它根本就没有这个概念,所以可以将任意编码的字符串存入其中。但是为了程序的规范性和可维护性,在同一套程序中应该遵守使用统一的编码,例如,统一使用UTF-8编码就是一种很好的约定。所以在对数据操作的时候我们不再需要去关注编码和乱码问题。

  b)、FixedString,类型和传统意义上的Char类型有些类似,对于一些字符有明确长度的场合,可以使用固定长度的字符串。定长字符串通过FixedString(N)声明,其中N表示字符串长度,但是与Char不同的是,FixedString使用Null字节填充末尾字符,而Char通常使用空格填充。

代码语言:javascript复制
String                  -- 字符串数据类型   一般情况下使用String;类型就可以
FixedString(n)          -- 固定长度的数据类型

使用案例,如下所示:

代码语言:javascript复制
 1 master :) drop table if exists tb_stu ;
 2 
 3 DROP TABLE IF EXISTS tb_stu
 4 
 5 Ok.
 6 
 7 0 rows in set. Elapsed: 0.013 sec. 
 8 
 9 master :) create table if not exists tb_stu(
10 :-] sid FixedString(8) ,
11 :-] name String ,
12 :-] address String
13 :-] ) engine = TinyLog ;
14 
15 CREATE TABLE IF NOT EXISTS tb_stu
16 (
17     `sid` FixedString(8),
18     `name` String,
19     `address` String
20 )
21 ENGINE = TinyLog
22 
23 Ok.
24 
25 0 rows in set. Elapsed: 0.013 sec. 
26 
27 master :)  
28 master :) insert into tb_stu values('aaaaaaaa' , 'HANGGE' ,'ZhongNanHai') ;
29 
30 INSERT INTO tb_stu VALUES
31 
32 Ok.
33 
34 1 rows in set. Elapsed: 0.011 sec. 
35 
36 master :) select * from tb_stu;
37 
38 SELECT *
39 FROM tb_stu
40 
41 ┌─sid──────┬─name───┬─address─────┐
42 │ aaaaaaaa │ HANGGE │ ZhongNanHai │
43 └──────────┴────────┴─────────────┘
44 
45 1 rows in set. Elapsed: 0.008 sec. 
46 
47 master :) 

4.5、UUID随机的一串字符串。

代码语言:javascript复制
 1 master :) drop table if exists tb_uuid ;
 2 
 3 DROP TABLE IF EXISTS tb_uuid
 4 
 5 Ok.
 6 
 7 0 rows in set. Elapsed: 0.002 sec. 
 8 
 9 master :) create table tb_uuid(
10 :-] id UUID ,
11 :-] name String
12 :-] ) engine = TinyLog ;
13 
14 CREATE TABLE tb_uuid
15 (
16     `id` UUID,
17     `name` String
18 )
19 ENGINE = TinyLog
20 
21 Ok.
22 
23 0 rows in set. Elapsed: 0.003 sec. 
24 
25 master :)  
26 master :) insert into tb_uuid (name) values ('zss') , ('lss') ,('daa') ;
27 
28 INSERT INTO tb_uuid (name) VALUES
29 
30 Ok.
31 
32 3 rows in set. Elapsed: 0.004 sec. 
33 
34 master :) insert into tb_uuid select generateUUIDv4() , 'HANGGE' ;
35 
36 INSERT INTO tb_uuid SELECT 
37     generateUUIDv4(),
38     'HANGGE'
39 
40 Ok.
41 
42 0 rows in set. Elapsed: 0.010 sec. 
43 
44 master :) select * from tb_uuid;
45 
46 SELECT *
47 FROM tb_uuid
48 
49 ┌───────────────────────────────────id─┬─name───┐
50 │ 00000000-0000-0000-0000-000000000000 │ zss    │
51 │ 00000000-0000-0000-0000-000000000000 │ lss    │
52 │ 00000000-0000-0000-0000-000000000000 │ daa    │
53 │ c712b9e8-1c6c-4614-a836-f85da3de62aa │ HANGGE │
54 └──────────────────────────────────────┴────────┘
55 
56 4 rows in set. Elapsed: 0.008 sec. 
57 
58 master :) 

4.6、枚举类型,包括 Enum8 和 Enum16 类型。Enum 保存 'string'= integer 的对应关系。Enum8 用 'String'= Int8 对描述。Enum16 用 'String'= Int16 对描述。

用法演示,创建一个带有一个枚举 Enum8('hello' = 1, 'world' = 2) 类型的列:

代码语言:javascript复制
 1 master :) CREATE TABLE t_enum
 2 :-] (
 3 :-]     x Enum8('hello' = 1, 'world' = 2)
 4 :-] )
 5 :-] ENGINE = TinyLog;
 6 
 7 CREATE TABLE t_enum
 8 (
 9     `x` Enum8('hello' = 1, 'world' = 2)
10 )
11 ENGINE = TinyLog
12 
13 Ok.
14 
15 0 rows in set. Elapsed: 0.008 sec. 
16 
17 master :) 
18 
19 -- 这个 x 列只能存储类型定义中列出的值:'hello'或'world'。如果尝试保存任何其他值,ClickHouse 抛出异常。
20 master :) INSERT INTO t_enum VALUES ('hello'), ('world'), ('hello')
21 :-] ;
22 
23 INSERT INTO t_enum VALUES
24 
25 Ok.
26 
27 3 rows in set. Elapsed: 0.006 sec. 
28 
29 master :) INSERT INTO t_enum VALUES;
30 
31 INSERT INTO t_enum VALUES
32 
33 Ok.
34 
35 0 rows in set. Elapsed: 0.004 sec. 
36 
37 master :) insert into t_enum values('a')
38 :-] ;
39 
40 INSERT INTO t_enum VALUES
41 
42 
43 Exception on client:
44 Code: 36. DB::Exception: Unknown element 'a' for type Enum8('hello' = 1, 'world' = 2)
45 
46 Connecting to localhost:9000 as user default.
47 Connected to ClickHouse server version 20.8.3 revision 54438.
48 
49 master :) 
50 
51 
52 -- 从表中查询数据时,ClickHouse 从 Enum 中输出字符串值。
53 master :) select * from t_enum;
54 
55 SELECT *
56 FROM t_enum
57 
58 ┌─x─────┐
59 │ hello │
60 │ world │
61 │ hello │
62 └───────┘
63 
64 3 rows in set. Elapsed: 0.009 sec. 
65 
66 master :) 
67 
68 
69 -- 如果需要看到对应行的数值,则必须将 Enum 值转换为整数类型。cast 强制数据类型转换... 将枚举类型字段转换成Int8数据类型
70 master :) SELECT CAST(x, 'Int8') FROM t_enum
71 :-] ;
72 
73 SELECT CAST(x, 'Int8')
74 FROM t_enum
75 
76 ┌─CAST(x, 'Int8')─┐
77 │               1 │
78 │               2 │
79 │               1 │
80 └─────────────────┘
81 
82 3 rows in set. Elapsed: 0.006 sec. 
83 
84 master :) 

4.7、数组数据类型。Array(T):由 T 类型元素组成的数组。其中,T 可以是任意类型,包含数组类型。 但不推荐使用多维数组,ClickHouse 对多维数组的支持有限。

例如,不能在 MergeTree 表中存储多维数组。

代码语言:javascript复制
Clickhouse支持数组这种复合数据类型,并且数据操作在数据分析中起到非常便利的效果,数组的定义方式有两种:
第一种是array(T),泛型的方式。
第二种是直接插入数据的行式[e1,e2,e3.....],我们在这里要求数组中的数据类型是一致的,数组是强数据类型。

使用案例,如下所示:

代码语言:javascript复制
 1 master :) SELECT array(1, 2) AS x, toTypeName(x);
 2 
 3 SELECT 
 4     [1, 2] AS x,
 5     toTypeName(x)
 6 
 7 ┌─x─────┬─toTypeName(array(1, 2))─┐
 8 │ [1,2] │ Array(UInt8)            │
 9 └───────┴─────────────────────────┘
10 
11 1 rows in set. Elapsed: 0.022 sec. 
12 
13 master :) SELECT [1, 2] AS x, toTypeName(x);
14 
15 SELECT 
16     [1, 2] AS x,
17     toTypeName(x)
18 
19 ┌─x─────┬─toTypeName([1, 2])─┐
20 │ [1,2] │ Array(UInt8)       │
21 └───────┴────────────────────┘
22 
23 1 rows in set. Elapsed: 0.009 sec. 
24 
25 
26 -- 数组的取值,从1开始取值,中括号里面从1开始,就可以取出数组的第一个值。
27 
28 master :) SELECT ['a', 'b', 'c'][1]
29 :-] ;
30 
31 SELECT ['a', 'b', 'c'][1]
32 
33 ┌─arrayElement(['a', 'b', 'c'], 1)─┐
34 │ a                                │
35 └──────────────────────────────────┘
36 
37 1 rows in set. Elapsed: 0.006 sec. 
38 
39 master :)  select array('a','b','c')[2];
40 
41 SELECT ['a', 'b', 'c'][2]
42 
43 ┌─arrayElement(array('a', 'b', 'c'), 2)─┐
44 │ b                                     │
45 └───────────────────────────────────────┘
46 
47 1 rows in set. Elapsed: 0.007 sec. 
48 
49 master :) 

如果要创建Array数组类型的字段,需要使用()来指定泛型,比如Array(String),如下所示:

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_array
 3 :-] (
 4 :-]     `id` UInt8,
 5 :-] `name` String,
 6 :-] `hobby` Array(String)
 7 :-] )ENGINE = Log;
 8 
 9 CREATE TABLE tb_array
10 (
11     `id` UInt8,
12     `name` String,
13     `hobby` Array(String)
14 )
15 ENGINE = Log
16 
17 Ok.
18 
19 0 rows in set. Elapsed: 0.029 sec. 
20 
21 master :) desc tb_array;
22 
23 DESCRIBE TABLE tb_array
24 
25 ┌─name──┬─type──────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
26 │ id    │ UInt8         │              │                    │         │                  │                │
27 │ name  │ String        │              │                    │         │                  │                │
28 │ hobby │ Array(String) │              │                    │         │                  │                │
29 └───────┴───────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
30 
31 3 rows in set. Elapsed: 0.014 sec. 
32 
33 master :) insert into tb_array values(1,'张三',['抽烟','喝酒','烫头']),(2,'岳岳',['说相声','唱歌']);
34 
35 INSERT INTO tb_array VALUES
36 
37 Ok.
38 
39 2 rows in set. Elapsed: 0.045 sec. 
40 
41 master :) 
42 master :) select * from tb_array;
43 
44 SELECT *
45 FROM tb_array
46 
47 ┌─id─┬─name─┬─hobby──────────────────┐
48 │  1 │ 张三 │ ['抽烟','喝酒','烫头'] │
49 │  2 │ 岳岳 │ ['说相声','唱歌']      │
50 └────┴──────┴────────────────────────┘
51 
52 2 rows in set. Elapsed: 0.071 sec. 
53 
54 master :) 

注意:根据数组创建的数据块数,会根据服务器的核数来决定创建几个块,服务器的核数决定了创建的块数。

代码语言:javascript复制
 1 总核数 = 物理CPU个数 * 每颗物理CPU的核数。
 2 总逻辑CPU数 = 物理CPU个数 * 每颗物理CPU的核数 * 超线程数。
 3 
 4 查看物理CPU个数。
 5 cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l
 6 
 7 查看每个物理CPU中的core的个数即核数。
 8 cat /proc/cpuinfo | grep "cpu cores" |uniq
 9 
10 查看逻辑CPU的个数
11 cat /proc/cpuinfo | grep "processor" | wc -l

数组居然还支持高阶函数,类似Java的lambda表达式,也是很强大的东西了,如下所示:

注意:Clickhouse的字符串不能使用双引号引起来,只能使用单引号引起来。

代码语言:javascript复制
 1 master :) 
 2 master :) select arrayMap(e -> concat(e,'lambda'),hobby) from tb_array;
 3 
 4 SELECT arrayMap(e -> concat(e, 'lambda'), hobby)
 5 FROM tb_array
 6 
 7 ┌─arrayMap(lambda(tuple(e), concat(e, 'lambda')), hobby)─┐
 8 │ ['抽烟lambda','喝酒lambda','烫头lambda']               │
 9 │ ['说相声lambda','唱歌lambda']                          │
10 └────────────────────────────────────────────────────────┘
11 
12 2 rows in set. Elapsed: 0.048 sec. 
13 
14 master :) 

4.8、Nested数据类型,Nested(name1 Type1, Name2 Type2, …)。

  Nested是一种嵌套表结构,一张数据表,可以定义任意多个嵌套类型字段,但是每个字段的嵌套层级只支持一级,即嵌套表内不能继续使用嵌套类型,对于简单场景的层级关系或者关联关系,使用嵌套类型也是一个不错的选择。

Nested嵌套类型本质是一种多维数组的结构,嵌套表中的每个字段的都是一个数组,并且行与行之间数组的长度无须对齐,但是需要注意的是,在同一行数据内每个数组字段的长度必须相等。

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_nested
 3 :-] (
 4 :-]     `id` String,
 5 :-]     `user` Nested(    uid Int,     name String,     age UInt8)
 6 :-] )ENGINE = TinyLog;
 7 
 8 CREATE TABLE tb_nested
 9 (
10     `id` String,
11     `user` Nested(    uid Int,     name String,     age UInt8)
12 )
13 ENGINE = TinyLog
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.010 sec. 
18 
19 master :) desc tb_nested ;
20 
21 DESCRIBE TABLE tb_nested
22 
23 ┌─name──────┬─type──────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
24 │ id        │ String        │              │                    │         │                  │                │
25 │ user.uid  │ Array(Int32)  │              │                    │         │                  │                │
26 │ user.name │ Array(String) │              │                    │         │                  │                │
27 │ user.age  │ Array(UInt8)  │              │                    │         │                  │                │
28 └───────────┴───────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
29 
30 4 rows in set. Elapsed: 0.012 sec. 
31 
32 master :) 

插入数据,进行简单的测试,如下所示:

代码语言:javascript复制
 1 master :) insert into tb_nested values(1 , [1,2,3],['zss','lss','ymm'],[21,33,18]) ;
 2 
 3 INSERT INTO tb_nested VALUES
 4 
 5 Ok.
 6 
 7 1 rows in set. Elapsed: 0.007 sec. 
 8 
 9 master :) SELECT 
10 :-]     user.uid,
11 :-]     user.name
12 :-] FROM tb_nested;
13 
14 SELECT 
15     user.uid,
16     user.name
17 FROM tb_nested
18 
19 ┌─user.uid─┬─user.name───────────┐
20 │ [1,2,3]  │ ['zss','lss','ymm'] │
21 └──────────┴─────────────────────┘
22 
23 1 rows in set. Elapsed: 0.008 sec. 
24 
25 master :) 

4.9、元组,Tuple(T1, T2, ...):元组,其中每个元素都有单独的类型。

  Tuple元组数据类型,是一个特殊的数据类型,可以理解为集合,可以存储任意的数据类型,这一点区别于数组。在定义的时候需要声明数据类型和数据元素的个数。

数据类型由1~n个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容。元素同样支持类型推断,其推断依据仍然以最小存储代价为原则。与数据类型,元组也可以使用两种方式定义,常规方式是tuple(T),元组中可以存储多种数据类型,但是要注意数据类型的顺序。

使用案例,如下所示:

代码语言:javascript复制
 1 master :) 
 2 master :) SELECT tuple(1,'a') AS x, toTypeName(x);  # SELECT tuple(1,'a',12.23)可以简写为SELECT (1,'a',12.23)
 3 
 4 SELECT 
 5     (1, 'a') AS x,
 6     toTypeName(x)
 7 
 8 ┌─x───────┬─toTypeName(tuple(1, 'a'))─┐
 9 │ (1,'a') │ Tuple(UInt8, String)      │
10 └─────────┴───────────────────────────┘
11 
12 1 rows in set. Elapsed: 0.008 sec. 
13 
14 master :) 

注意:创建数据表的时候使用元组的时候,需要指定元素的数据类型。

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_tuple
 3 :-] (
 4 :-]     `id` UInt8,
 5 :-] `t1` Tuple(String,Int8,Date) 
 6 :-] )ENGINE = Log;
 7 
 8 CREATE TABLE tb_tuple
 9 (
10     `id` UInt8,
11     `t1` Tuple(String, Int8, Date)
12 )
13 ENGINE = Log
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.066 sec. 
18 
19 master :) insert into tb_tuple values(1, ('red',25,'1994-05-18'));
20 
21 INSERT INTO tb_tuple VALUES
22 
23 Ok.
24 
25 1 rows in set. Elapsed: 0.145 sec. 
26 
27 master :) select * from tb_tuple;
28 
29 SELECT *
30 FROM tb_tuple
31 
32 ┌─id─┬─t1──────────────────────┐
33 │  1 │ ('red',25,'1994-05-18') │
34 └────┴─────────────────────────┘
35 
36 1 rows in set. Elapsed: 0.033 sec. 
37 
38 master :) 

4.10、Date日期类型、DateTime日期时间类型,支持符合格式的字符串类型的插入。

DateTime64可以记录亚秒,它在DateTime之上增加了精度的设置,在指定数据类型的时候可以使用DateTime64(2)指定两位精度。

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_name
 3 :-] (
 4 :-]     `id` UInt8,
 5 :-]     `age` UInt8,
 6 :-]     `birthday` Date,
 7 :-] `updateTime` DateTime
 8 :-] )ENGINE = Log;
 9 
10 CREATE TABLE tb_name
11 (
12     `id` UInt8,
13     `age` UInt8,
14     `birthday` Date,
15     `updateTime` DateTime
16 )
17 ENGINE = Log
18 
19 Ok.
20 
21 0 rows in set. Elapsed: 0.055 sec. 
22 
23 master :) 
24 master :) insert into tb_name values(1,22,'1994-05-16','2021-02-20 14:21:30'),(2,24,'1994-05-17','2021-02-20 14:21:30');
25 
26 INSERT INTO tb_name VALUES
27 
28 Ok.
29 
30 2 rows in set. Elapsed: 0.065 sec. 
31 
32 master :) select * from tb_name;
33 
34 SELECT *
35 FROM tb_name
36 
37 ┌─id─┬─age─┬───birthday─┬──────────updateTime─┐
38 │  1 │  22 │ 1994-05-16 │ 2021-02-20 14:21:30 │
39 │  2 │  24 │ 1994-05-17 │ 2021-02-20 14:21:30 │
40 └────┴─────┴────────────┴─────────────────────┘
41 
42 2 rows in set. Elapsed: 0.010 sec. 
43 
44 master :) 

4.11、Enum枚举类型,Clickhouse支持枚举类型,这是一种在定义常量的时候经常会使用的数据类型,Clickhouse提供了Enum8和Enum16两种枚举类型,他们除了取值范围不同之外,别无二致。枚举固定使用(String:Int),这种Key/Value键值对的行式定义数据,所以Enum8和Enum16分别会对应(String:Int8)和(String:Int16)。

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_enum
 3 :-] (
 4 :-]     `id` UInt8,
 5 :-] `color` Enum('red' = 1,'green' = 2, 'blue' = 3)
 6 :-] )ENGINE = Log;
 7 
 8 CREATE TABLE tb_enum
 9 (
10     `id` UInt8,
11     `color` Enum('red' = 1, 'green' = 2, 'blue' = 3)
12 )
13 ENGINE = Log
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.034 sec. 
18 
19 master :) insert into tb_enum values(1,'red'),(2,'green'),(3,'blue');
20 
21 INSERT INTO tb_enum VALUES
22 
23 Ok.
24 
25 3 rows in set. Elapsed: 0.055 sec. 
26 
27 master :) insert into tb_enum values(4,1),(5,2),(6,3);
28 
29 INSERT INTO tb_enum VALUES
30 
31 Ok.
32 
33 3 rows in set. Elapsed: 0.035 sec. 
34 
35 master :) 

4.12、Domain(pojo、beans实体类)里面的IPv4数据类型,域名分为IPv4和IPv6两类,本质上它们是对整数和字符串的进一步封装,其中IPv4类型是基于UInt32封装的。

  1)、出于便捷性的考量,例如IPv4类型支持格式检查,格式错误的IP数据是无法被写入的。

  2)、出于性能的考量,同样以IPv4为例,IPv4使用UInt32存储,相对String更加紧凑,占用空间更小,查询性能更快。IPv6类型是基于FixedString(16)封装的,它的使用彷佛与IPv4别无二致,在使用Domain类型的时候还有一点需要注意,虽然它从表象上看起来与String一样,但是Domain类型并不是字符串,所以它不支持隐式的自动类型转换。如果需要返回IP的字符串行式,需要显示调用IPv4NumToString或者IPv6NumToString函数进行转换。

代码语言:javascript复制
 1 master :) 
 2 master :) CREATE TABLE tb_domin
 3 :-] (
 4 :-]     `id` UInt8,
 5 :-] `ip` IPv4
 6 :-] )ENGINE = Log;
 7 
 8 CREATE TABLE tb_domin
 9 (
10     `id` UInt8,
11     `ip` IPv4
12 )
13 ENGINE = Log
14 
15 Ok.
16 
17 0 rows in set. Elapsed: 0.024 sec. 
18 
19 master :) insert into tb_domin values(1, '192.168.110.133');
20 
21 INSERT INTO tb_domin VALUES
22 
23 Ok.
24 
25 1 rows in set. Elapsed: 0.058 sec. 
26 
27 master :) insert into tb_domin values(1, '192.168.110.133');
28 
29 INSERT INTO tb_domin VALUES
30 
31 Ok.
32 
33 1 rows in set. Elapsed: 0.006 sec. 
34 
35 master :) select * from tb_domin;
36 
37 SELECT *
38 FROM tb_domin
39 
40 ┌─id─┬──────────────ip─┐
41 │  1 │ 192.168.110.133 │
42 └────┴─────────────────┘
43 ┌─id─┬──────────────ip─┐
44 │  1 │ 192.168.110.133 │
45 └────┴─────────────────┘
46 
47 2 rows in set. Elapsed: 0.025 sec. 
48 
49 master :) 

5、Clickhouse为什么可以这么快?主要特点,如下所示:

1)、开发语言是C ,可以更好的利用硬件优势来提升数据处理的效率。   2)、摒弃了hadoop生态体系。   3)、数据底层以列式数据存储。   4)、可以利用单节点的多核并行处理。   5)、为数据建立索引,分为一级索引、二级索引、稀疏索引。   6)、使用大量高效率算法处理数据。   7)、支持向量化处理数据。   8)、支持预先运算模型,预先计算,等等优势。

6、Clickhouse的引擎分为数据库引擎和数据表引擎,数据表的引擎,不同的引擎决定数据库的类型,Clickhouse的数据库引擎的作用。

  1)、引擎决定了数据存储位置。   2)、数据组织结构。   3)、是否分块、是否索引、是否持久化操作。   4)、是否可以并发读写。   5)、是否支持副本操作、是否支持索引。   6)、是否支持分布式。

7、Clickhouse的数据表引擎(即表的类型)决定了的特征,如下所示:

  1)、数据的存储方式和位置,写到哪里以及从那里读取数据。   2)、支持那些查询以及如何支持。   3)、并发数据访问。   4)、索引的使用(如果存在)。   5)、是否可以执行多线程请求。   6)、数据复制参数。在读取时候,引擎只需要输出所请求的列,但在某些情况下,引擎可以在响应请求时部分处理数据。对于大多数正式的任务,应该使用MergeTree族中的引擎。

8、数据库,数据库起到了命名空间的作用,可以有效规避命名冲突的问题,也是为后续的数据隔离提供了支撑。任何一张数据表,都必须归属在某个数据库之下。在Clickhouse中数据库也有自己的引擎,数据库目前支持的数据库引擎有6种,如下所示:

1)、Ordinary,默认引擎,在绝大多数情况下我们都会使用默认引擎,使用的时候无须刻意声明。在此数据库下可以使用任意类型的表引擎。

  2)、Dictionary,字段引擎,此类数据库会自动为所有数据字典创建它们的数据表。

  3)、Memory,内存引擎,了解即可,用于存放临时数据,此类数据库下的数据表只会停留在内存中,不会设计任何磁盘操作,当服务重启后数据会被清除。

  4)、Lazy,日志引擎,此类数据库下只能使用Log系列的表引擎。

  5)、Mysql,Mysql数据库的引擎,此类数据库下会自动拉取远端Mysql数据库中的数据,并为他们创建Mysql表引擎的数据表。

  6)、MaterializeMySQL,主要做Mysql的数据同步,Clickhouse 20.8将新增 MaterializeMySQL引擎 ,可通过binlog日志实时物化mysql数据,极大提升了数仓的查询性能和数据同步的时效性;原有mysql中承担的数据分析工作 可交由clickhouse去做,这么做可显著降低线上mysql的负载,从此OLTP与OLAP业务实现完美融合。

9、Clickhouse的数据表引擎,具体分类,如下所示:

9.1)、Log日志引擎,具有最小功能的轻量级引擎。当您需要快速写入许多小表(最多约100万行)并在以后整体读取它们时,该类型的引擎是最有效的。具体表现为,比较简单,数据少,测试使用,Log家族都属于本地表(在/var/lib/clickhouse/data下面),本地存储表数据。

  1)、TinyLog引擎(数据不分快)。最简单的数据表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入的时候,数据将附加到文件末尾。该引擎没有并发控制,只支持并发读。如果同时从表中读取和写入数据,则读取操作将抛出异常。如果同时写入多个查询中的表,则数据将被破坏。

     注意:TinyLog引擎(数据不分快)引擎,*.bin每个字段的压缩数据,数据插入时会进行追加数据。sizes.json文件,显示每个字段文件的大小。该引擎的特点,没有数据块,不能并行操作,不能同时读写操作,没有索引,写是追加写,数据以列字段文件存储。

代码语言:javascript复制
 1 master :) 
 2 master :) 
 3 master :) CREATE TABLE tb_tinyLog
 4 :-] (
 5 :-]     `id` UInt8,
 6 :-] `log_name` String
 7 :-] )ENGINE = TinyLog;
 8 
 9 CREATE TABLE tb_tinyLog
10 (
11     `id` UInt8,
12     `log_name` String
13 )
14 ENGINE = TinyLog
15 
16 Ok.
17 
18 0 rows in set. Elapsed: 0.016 sec. 
19 
20 master :) insert into tb_tinyLog values(1, '张三三');
21 
22 INSERT INTO tb_tinyLog VALUES
23 
24 Ok.
25 
26 1 rows in set. Elapsed: 0.027 sec. 
27 
28 master :) select * from tb_tinyLog;
29 
30 SELECT *
31 FROM tb_tinyLog
32 
33 ┌─id─┬─log_name─┐
34 │  1 │ 张三三   │
35 └────┴──────────┘
36 
37 1 rows in set. Elapsed: 0.010 sec. 
38 
39 master :) 

  2)、StripeLog引擎,数据分块列在一起,在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。

  3)、Log引擎。

9.2)、MergeTree引擎,主要包含下面几种。

  1)、MergeTree()。

  2)、ReplacingMergeTree。

  3)、SummingMergeTree引擎,将相同主键的数据分区内合并指定字段进行累加。

  4)、CollapsingMergeTree,ClickHouse实现了CollapsingMergeTree来消除ReplacingMergeTree的限制(只删除小版本字段的问题)。该引擎要求在建表语句中指定一个标记列Sign,后台Compaction时会将主键相同、Sign相反的行进行折叠,也即删除。

     CollapsingMergeTree虽然解决了主键相同的数据即时删除的问题,但是状态持续变化且多线程并行写入情况下,状态行与取消行位置可能乱序,导致无法正常折叠。只有保证老的状态行在在取消行的上面, 新的状态行在取消行的下面,但是多线程无法保证写的顺序。

  5)、VersionedCollapsingMergeTree,取消字段和数据版本同事使用,避免取消行数据无法删除的问题,为了解决CollapsingMergeTree乱序写入情况下无法正常折叠问题,VersionedCollapsingMergeTree表引擎在建表语句中新增了一列Version,用于在乱序情况下记录状态行与取消行的对应关系。主键相同,且Version相同、Sign相反的行,在Compaction时会被删除。与CollapsingMergeTree类似, 为了获得正确结果,业务层需要改写SQL,将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。

0 人点赞