一、表引擎

### --- 表引擎(即表的类型)决定了:
~~~ 数据的存储方式和位置,写到哪里以及从哪里读取数据
~~~ 支持哪些查询以及如何支持。
~~~ 并发数据访问。
~~~ 索引的使用(如果存在)。
~~~ 是否可以执行多线程请求。
~~~ 数据复制参数。

~~~ # ClickHouse的表引擎有很多,下面介绍其中几种,
~~~ # 对其他引擎有兴趣的可以去查阅官方文档
~~~ # https://clickhouse.yandex/docs/zh/operations/table_engines/

二、日志

### --- TinyLog

~~~ 最简单的表引擎,用于将数据存储在磁盘上。
~~~ 每列都存储在单独的压缩文件中,写入时,数据将附加到文件末尾。该引擎没有并发控制
~~~ 如果同时从表中读取和写入数据,则读取操作将抛出异常;
~~~ 如果同时写入多个查询中的表,则数据将被破坏。
~~~ 这种表引擎的典型用法是 write-once:首先只写入一次数据,然后根据需要多次读取。
~~~ 此引擎适用于相对较小的表(建议最多1,000,000行)。
~~~ 如果有许多小表,则使用此表引擎是适合的,因为它需要打开的文件更少。
~~~ 当拥有大量小表时,可能会导致性能低下。 不支持索引。
### --- 案例:创建一个TinyLog引擎的表并插入一条数据

hadoop01 :) create table t (a UInt16, b String) ENGINE=TinyLog;

CREATE TABLE t
(
`a` UInt16,
`b` String
)
ENGINE = TinyLog

Ok.
hadoop01 :) insert into t (a, b) values (1, 'abc');

INSERT INTO t (a, b) VALUES

Ok.
### --- 此时我们到保存数据的目录/var/lib/clickhouse/data/default/t中可以看到如下目录结构:

~~~ # a.bin 和 b.bin 是压缩过的对应的列的数据, sizes.json 中记录了每个 *.bin 文件的大小:
[root@hadoop01 ~]# ls /var/lib/clickhouse/data/default/t
a.bin b.bin sizes.json

[root@hadoop01 ~]# cat /var/lib/clickhouse/data/default/t/sizes.json
{"yandex":{"a%2Ebin":{"size":"28"},"b%2Ebin":{"size":"30"}}}

三、log

### --- Log

~~~ Log与 TinyLog 的不同之处在于,«标记» 的小文件与列文件存在一起。
~~~ 这些标记写在每个数据块上,并且包含偏移量,
~~~ 这些偏移量指示从哪里开始读取文件以便跳过指定的行数。
~~~ 这使得可以在多个线程中读取表数据。
~~~ 对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。
~~~ Log 引擎不支持索引。同样,如果写入表失败,则该表将被破坏,并且从该表读取将返回错误。
~~~ Log 引擎适用于临时数据,write-once 表以及测试或演示目的。
### --- StripeLog

~~~ 该引擎属于日志引擎系列。
~~~ 请在日志引擎系列文章中(https://clickhouse.tech/docs/zh/engines/table-engines/log-family/)
~~~ 查看引擎的共同属性和差异。
~~~ 在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。
### --- 建表

~~~ # 查看建表请求的详细说明。
(https://clickhouse.tech/docs/zh/engines/table-engines/logfamily/stripelog/#createtable-query)

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
column1_name [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
column2_name [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = StripeLog
;
~~~     # 写数据

~~~ StripeLog 引擎将所有列存储在一个文件中。
~~~ 对每一次 Insert 请求,ClickHouse 将数据块追加在表文件的末尾,逐列写入。
~~~     # ClickHouse 为每张表写入以下文件:

~~~ data.bin — 数据文件。
~~~ index.mrk — 带标记的文件。标记包含了已插入的每个数据块中每列的偏移量。
~~~ StripeLog 引擎不支持 ALTER UPDATE 和 ALTER DELETE 操作。
# 读数据
带标记的文件使得 ClickHouse 可以并行的读取数据。
这意味着 SELECT 请求返回行的顺序是不可预测的。使用ORDER BY 子句对行进行排序。
### --- 使用示例

~~~ # 建表
hadoop01 :) CREATE TABLE stripe_log_table ( timestamp DateTime, message_type String, message String ) ENGINE = StripeLog;

CREATE TABLE stripe_log_table
(
`timestamp` DateTime,
`message_type` String,
`message` String
)
ENGINE = StripeLog

Ok.
~~~     # 插入数据:

hadoop01 :) INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The first regular message');

INSERT INTO stripe_log_table VALUES

Ok.
hadoop01 :) INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The second regular message');

INSERT INTO stripe_log_table VALUES

Ok.
hadoop01 :) INSERT INTO stripe_log_table VALUES (now(),'WARNING','The first warning message');

INSERT INTO stripe_log_table VALUES

Ok.
~~~     我们使用两次 INSERT 请求从而在 data.bin 文件中创建两个数据块。
~~~ ClickHouse 在查询数据时使用多线程。
~~~ 每个线程读取单独的数据块并在完成后独立的返回结果行。
~~~ 这样的结果是,大多数情况下,输出中块的顺序和输入时相应块的顺序是不同的。例如:
hadoop01 :) SELECT * FROM stripe_log_table;

SELECT *
FROM stripe_log_table

┌───────────timestamp─┬─message_type─┬─message───────────────────┐
│ 2021-10-31 23:43:17 │ REGULAR │ The first regular message │
└─────────────────────┴──────────────┴───────────────────────────┘
┌───────────timestamp─┬─message_type─┬─message────────────────────┐
│ 2021-10-31 23:43:32 │ REGULAR │ The second regular message │
└─────────────────────┴──────────────┴────────────────────────────┘
┌───────────timestamp─┬─message_type─┬─message───────────────────┐
│ 2021-10-31 23:46:18 │ WARNING │ The first warning message │
└─────────────────────┴──────────────┴───────────────────────────┘
### --- 对结果排序(默认增序):

hadoop01 :) SELECT * FROM stripe_log_table ORDER BY timestamp ;

SELECT *
FROM stripe_log_table
ORDER BY timestamp ASC

┌───────────timestamp─┬─message_type─┬─message────────────────────┐
│ 2021-10-31 23:43:17 │ REGULAR │ The first regular message │
│ 2021-10-31 23:43:32 │ REGULAR │ The second regular message │
│ 2021-10-31 23:46:18 │ WARNING │ The first warning message │
└─────────────────────┴──────────────┴────────────────────────────┘

四、Memory

### --- Memory

~~~ 内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。
~~~ 读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过10G/s)。
~~~ 一般用到它的地方不多,除了用来测试,就是在需要非常高的性能,
~~~ 同时数据量又不太大(上限大概 1 亿行)的场景。

五、Merge

### --- Merge

~~~ Merge 引擎 (不要跟 MergeTree 引擎混淆) 本身不存储数据,
~~~ 但可用于同时从任意多个其他的表中读取数据。
~~~ 读是自动并行的,不支持写入。
~~~ 读取时,那些被真正读取到数据的表的索引(如果有的话)会被使用。
~~~ Merge 引擎的参数:一个数据库名和一个用于匹配表名的正则表达式。
### --- 案例:先建t1,t2,t3三个表,然后用 Merge 引擎的 t 表再把它们链接起来。

~~~ # 建表t1
hadoop01 :) create table t1 (id UInt16, name String) ENGINE=TinyLog;

CREATE TABLE t1
(
`id` UInt16,
`name` String
)
ENGINE = TinyLog

Ok.
~~~     # 建表t2

hadoop01 :) create table t2 (id UInt16, name String) ENGINE=TinyLog;

CREATE TABLE t2
(
`id` UInt16,
`name` String
)
ENGINE = TinyLog

Ok.
~~~     # 建表t3

hadoop01 :) create table t3 (id UInt16, name String) ENGINE=TinyLog;

CREATE TABLE t3
(
`id` UInt16,
`name` String
)
ENGINE = TinyLog

Ok.
~~~     # 在t1表中写入数据

hadoop01 :) insert into t1(id, name) values (1, 'first');

INSERT INTO t1 (id, name) VALUES

Ok.
~~~     # 在t2表中写入数据

hadoop01 :) insert into t2(id, name) values (2, 'second');

INSERT INTO t2 (id, name) VALUES

Ok.
~~~     # 在t3表中写入数据

hadoop01 :) insert into t3(id, name) values (3, 'i am in t3');

INSERT INTO t3 (id, name) VALUES

Ok.
~~~     # 创建t表

hadoop01 :) create table t (id UInt16, name String) ENGINE=Merge(currentDatabase(), '^t');

CREATE TABLE t
(
`id` UInt16,
`name` String
)
ENGINE = Merge(currentDatabase(), '^t')

Ok.
~~~     # 查看t表

hadoop01 :) select * from t;

┌─id─┬─name──┐
│ 1 │ first │
└────┴───────┘

┌─id─┬─name───┐
│ 2 │ second │
└────┴────────┘

┌─id─┬─name───────┐
│ 3 │ i am in t3 │
└────┴────────────┘











Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart

                                                                                                                                                   ——W.S.Landor