四 ClickHouse引擎詳解


表引擎是ClickHouse設計實現中的一大特色 ,數據表擁有何種特性、數據以何 種形式被存儲以及如何被加載。ClickHouse擁有非常龐大的表引擎體 系,截至本書完成時,其共擁有合並樹、外部存儲、內存、文件、接口 和其他6大類20多種表引擎。而在這眾多的表引擎中,又屬合並樹 (MergeTree)表引擎及其家族系列(*MergeTree)最為強大,在生產 環境的絕大部分場景中,都會使用此系列的表引擎。因為只有合並樹系 列的表引擎才支持主鍵索引、數據分區、數據副本和數據采樣這些特 性,同時也只有此系列的表引擎支持ALTER相關操作。

合並樹家族自身也擁有多種表引擎的變種。其中MergeTree作為家 族中最基礎的表引擎,提供了主鍵索引、數據分區、數據副本和數據采 樣等基本能力,而家族中其他的表引擎則在MergeTree的基礎之上各有 所長。例如ReplacingMergeTree表引擎具有刪除重復數據的特性,而 SummingMergeTree表引擎則會按照排序鍵自動聚合數據。如果給合並樹 系列的表引擎加上Replicated前綴,又會得到一組支持數據副本的表引 擎,例如ReplicatedMergeTree、ReplicatedReplacingMergeTree、 ReplicatedSummingMergeTree等。

表引擎(即表的類型)決定了:

  1. 數據的存儲方式和位置,寫到哪里以及從哪里讀取數據

  2. 支持哪些查詢以及如何支持。

  3. 並發數據訪問。

  4. 索引的使用(如果存在)。

  5. 是否可以執行多線程請求。

  6. 數據復制參數,是否可以存儲數據副本。

  7. 分布式引擎 實現分布式

... ...

1 Log系列引擎

Log家族具有最小功能的[輕量級引擎。當您需要快速寫入許多小表(最多約100萬行)並在以后整體讀取它們時,該類型的引擎是最有效的。

1.1 TinyLog引擎

最簡單的表引擎,用於將數據存儲在磁盤上。每列都存儲在單獨的壓縮文件中,寫入時,數據將附加到文件末尾。該引擎沒有並發控制

 1、最簡單的引擎
 2、沒有索引,沒有標記塊
 3、寫是追加寫
 4、數據以列字段文件存儲
 5、不允許同時讀寫

 

-- 建表 
create table test_tinylog(
    id UInt8 ,
    name String ,
    age UInt8
)engine=TinyLog ;
-- 查看表結構
desc test_tinylog ;
-- 查看建表語句 
SHOW CREATE TABLE test_tinylog ;
-- 插入數據 
insert into test_tinylog values(1,'liubei',45),(2,'guanyu',43),(3,'zhangfei',41) ;
​
SELECT *
FROM test_tinylog
​
┌─id─┬─name─────┬─age─┐
│  1 │ liubei   │  45 │
│  2 │ guanyu   │  43 │
│  3 │ zhangfei │  41 │
└────┴──────────┴─────┘

  

查看數底層存儲

[root@doit01 test_tinylog]# pwd
/var/lib/clickhouse/data/default/test_tinylog
-rw-r-----. 1 clickhouse clickhouse 29 May 19 15:29 age.bin
-rw-r-----. 1 clickhouse clickhouse 29 May 19 15:29 id.bin
-rw-r-----. 1 clickhouse clickhouse 50 May 19 15:29 name.bin
-rw-r-----. 1 clickhouse clickhouse 90 May 19 15:29 sizes.json
-- 當再次插入數據以后 , 在每個文件中追加寫入的
-rw-r-----. 1 clickhouse clickhouse  58 May 19 15:31 age.bin
-rw-r-----. 1 clickhouse clickhouse  58 May 19 15:31 id.bin
-rw-r-----. 1 clickhouse clickhouse 100 May 19 15:31 name.bin
-rw-r-----. 1 clickhouse clickhouse  91 May 19 15:31 sizes.json

  

insert into t select * from t 會將表存儲結構損壞  :  刪除表目錄 刪除元數據 

1.2 StripeLog引擎

 1、data.bin存儲所有數據
 2、index.mrk 對數據建立索引
 3、size.json 數據大小
 4、並發讀寫

  

create table test_stripelog(
    id UInt8 ,
    name String ,
    age UInt8
)engine=StripeLog ;
-- 插入數據 
insert into test_stripelog values(1,'liubei',45),(2,'guanyu',43),(3,'zhangfei',41) ;

查看底層數據

/var/lib/clickhouse/data/default/test_stripelog
-rw-r-----. 1 clickhouse clickhouse 167 May 19 15:43 data.bin  存儲所有列的數據
-rw-r-----. 1 clickhouse clickhouse  75 May 19 15:43 index.mrk  記錄數據的索引信息
-rw-r-----. 1 clickhouse clickhouse  68 May 19 15:43 sizes.json  記錄文件內容的大小

1.3 Log引擎

日志與 TinyLog 的不同之處在於,«標記» 的小文件與列文件存在一起。這些標記寫在每個數據塊上,並且包含偏移量,這些偏移量指示從哪里開始讀取文件以便跳過指定的行數。這使得可以在多個線程中讀取表數據。對於並發數據訪問,可以同時執行讀取操作,而寫入操作則阻塞讀取和其它寫入。Log 引擎不支持索引。同樣,如果寫入表失敗,則該表將被破壞,並且從該表讀取將返回錯誤。Log 引擎適用於臨時數據,write-once 表以及測試或演示目的。

1、*.bin存儲每個字段的數據
 2、mark.mrk 數據塊標記
 3、支持多線程處理
 4、並發讀寫

  

create table test_log(
    id UInt8 ,
    name String ,
    age UInt8
)engine=Log ;
insert into test_log values(1,'liubei',45),(2,'guanyu',43),(3,'zhangfei',41) ;

  

查看數據

-rw-r-----. 1 clickhouse clickhouse  29 May 19 15:46 age.bin
-rw-r-----. 1 clickhouse clickhouse  29 May 19 15:46 id.bin
-rw-r-----. 1 clickhouse clickhouse  48 May 19 15:46 __marks.mrk
-rw-r-----. 1 clickhouse clickhouse  50 May 19 15:46 name.bin
-rw-r-----. 1 clickhouse clickhouse 120 May 19 15:46 sizes.json

  

Log 和 StripeLog 引擎支持:

並發訪問數據的鎖。

INSERT 請求執行過程中表會被鎖定,並且其他的讀寫數據的請求都會等待直到鎖定被解除。如果沒有寫數據的請求,任意數量的讀請求都可以並發執行。

並行讀取數據。

在讀取數據時,ClickHouse 使用多線程。 每個線程處理不同的數據塊。

Log 引擎為表中的每一列使用不同的文件。StripeLog 將所有的數據存儲在一個文件中。因此 StripeLog 引擎在操作系統中使用更少的描述符,但是 Log 引擎提供更高的讀性能。

TinyLog 引擎是該系列中最簡單的引擎並且提供了最少的功能和最低的性能。TingLog 引擎不支持並行讀取和並發數據訪問,並將每一列存儲在不同的文件中。它比其余兩種支持並行讀取的引擎的讀取速度更慢,並且使用了和 Log 引擎同樣多的描述符。你可以在簡單的低負載的情景下使用它。

2 MergeTree系列引擎

MergeTree系列的表引擎是ClickHouse數據存儲功能的核心。它們提供了用於彈性和高性能數據檢索的大多數功能:列存儲,自定義分區,稀疏的主索引,輔助數據跳過索引等。

基本[MergeTree表引擎可以被認為是單節點ClickHouse實例的默認表引擎,因為它在各種用例中通用且實用。

除了基礎表引擎MergeTree之 外,常用的表引擎還有ReplacingMergeTree、SummingMergeTree、 AggregatingMergeTree、CollapsingMergeTree和 VersionedCollapsingMergeTree。每一種合並樹的變種,在繼承了基 礎MergeTree的能力之后,又增加了獨有的特性。其名稱中的“合並” 二字奠定了所有類型MergeTree的基因,它們的所有特殊邏輯,都是在 觸發合並的過程中被激活的。

主要特點:

  • 存儲按主鍵排序的數據。

這使您可以創建一個小的稀疏索引,以幫助更快地查找數據。

  • 如果指定了[分區鍵]則可以使用[分區)。

ClickHouse支持的某些分區操作比對相同數據,相同結果的常規操作更有效。ClickHouse還會自動切斷在查詢中指定了分區鍵的分區數據。這也提高了查詢性能。

  • 數據復制支持。

ReplicatedMergeTree表族提供數據復制。有關更多信息.

  • 數據采樣支持。

如有必要,可以在表中設置數據采樣方法。

2.1 MergeTree引擎

MergeTree在寫入一批數據時,數據總會以數據片段的形式寫入磁盤,且數據片段不可修改。為了避免片段過多,ClickHouse會通過后台線程,定期合並這些數據片段,屬於相同分區的數據片段會被合成 一個新的片段。這種數據片段往復合並的特點,也正是合並樹名稱的由來。

語法

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster](
    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) 
ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...][SETTINGS name=value, ...]

  

MergeTree表引擎除了常規參數之外,還擁有一些獨有的配置選 項。接下來會着重介紹其中幾個重要的參數,

(1)PARTITION BY [選填]:分區鍵,用於指定表數據以何種標 准進行分區。分區鍵既可以是單個列字段,也可以通過元組的形式使 用多個列字段,同時它也支持使用列表達式。如果不聲明分區鍵,則 ClickHouse會生成一個名為all的分區。合理使用數據分區,可以有效 減少查詢時數據文件的掃描范圍,更多關於數據分區的細節會在6.2節 介紹。

(2)ORDER BY [必填]:排序鍵,用於指定在一個數據片段內, 數據以何種標准排序。默認情況下主鍵(PRIMARY KEY)與排序鍵相 同。排序鍵既可以是單個列字段,例如ORDER BY CounterID,也可以 通過元組的形式使用多個列字段,例如ORDER BY(CounterID,EventDate)。當使用多個列字段排序時,以ORDER BY(CounterID,EventDate)為例,在單個數據片段內,數據首先會以 CounterID排序,相同CounterID的數據再按EventDate排序。

(3)PRIMARY KEY [選填]:主鍵,顧名思義,聲明后會依照主鍵 字段生成一級索引,用於加速表查詢。默認情況下,主鍵與排序鍵 (ORDER BY)相同,所以通常直接使用ORDER BY代為指定主鍵,無須刻 意通過PRIMARY KEY聲明。所以在一般情況下,在單個數據片段內,數 據與一級索引以相同的規則升序排列。與其他數據庫不同,MergeTree 主鍵允許存在重復數據(ReplacingMergeTree可以去重)。

(4)SAMPLE BY [選填]:抽樣表達式,用於聲明數據以何種標准 進行采樣。如果使用了此配置項,那么在主鍵的配置中也需要聲明同 樣的表達式,例如:

省略... 
) ENGINE = MergeTree() 
ORDER BY (CounterID, EventDate, intHash32(UserID) SAMPLE BY intHash32(UserID) 

(5)SETTINGS:index_granularity [選填]: index_granularity對於MergeTree而言是一項非常重要的參數,它表 示索引的粒度,默認值為8192。也就是說,MergeTree的索引在默認情 況下,每間隔8192行數據才生成一條索引,其具體聲明方式如下所 示:

省略... 
) ENGINE = MergeTree() 
省略... 
SETTINGS index_granularity = 8192; 

8192是一個神奇的數字,在ClickHouse中大量數值參數都有它的 影子,可以被其整除(例如最小壓縮塊大小 min_compress_block_size:65536)。通常情況下並不需要修改此參 數,但理解它的工作原理有助於我們更好地使用MergeTree。關於索引 詳細的工作原理會在后續闡述。

(6)SETTINGS:index_granularity_bytes [選填]:在19.11版本之前,ClickHouse只支持固定大小的索引間隔,由 index_granularity控制,默認為8192。在新版本中,它增加了自適應 間隔大小的特性,即根據每一批次寫入數據的體量大小,動態划分間 隔大小。而數據的體量大小,正是由index_granularity_bytes參數控 制的,默認為10M(10×1024×1024),設置為0表示不啟動自適應功 能。

(7)SETTINGS:enable_mixed_granularity_parts [選填]:設 置是否開啟自適應索引間隔的功能,默認開啟。

(8)SETTINGS:merge_with_ttl_timeout [選填]:從19.6版本 開始,MergeTree提供了數據TTL的功能,

(9)SETTINGS:storage_policy [選填]:從19.15版本開始, MergeTree提供了多路徑的存儲策略,關於這部分的詳細介紹,

1) 創建表

drop table if exists tb_merge_tree ;
create table tb_merge_tree(
id Int8 ,
city String ,
ctime Date 
)
engine=MergeTree()
order by id 
partition by city ;
-- 查看建表語句
│ CREATE TABLE default.tb_merge_tree
(
    `id` Int8,
    `city` String,
    `ctime` Date
)
ENGINE = MergeTree()
PARTITION BY city
ORDER BY id
SETTINGS index_granularity = 8192 │

2)導入數據

insert into tb_merge_tree values(1,'BJ',now()) ,(2,'NJ',now()),(3,'DJ',now());
insert into tb_merge_tree values(4,'BJ',now()) ,(5,'NJ',now()),(6,'DJ',now());
insert into tb_merge_tree values(7,'BJ',now()) ,(8,'NJ',now()),(9,'DJ',now());
insert into tb_merge_tree values(10,'BJ',now()) ,(11,'NJ',now()),(12,'DJ',now());
┌─id─┬─city─┬──────ctime─┐
│  9 │ DJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  2 │ NJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  5 │ NJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│ 12 │ DJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  8 │ NJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│ 11 │ NJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  1 │ BJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  3 │ DJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  4 │ BJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  6 │ DJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  7 │ BJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│ 10 │ BJ   │ 2021-05-19 │
└────┴──────┴────────────┘

  

3)合並數據

optimize table tb_merge_tree final ;  一次性按照分區合並所有的數據
SELECT *
FROM tb_merge_tree
┌─id─┬─city─┬──────ctime─┐
│  3 │ DJ   │ 2021-05-19 │
│  6 │ DJ   │ 2021-05-19 │
│  9 │ DJ   │ 2021-05-19 │
│ 12 │ DJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  2 │ NJ   │ 2021-05-19 │
│  5 │ NJ   │ 2021-05-19 │
│  8 │ NJ   │ 2021-05-19 │
│ 11 │ NJ   │ 2021-05-19 │
└────┴──────┴────────────┘
┌─id─┬─city─┬──────ctime─┐
│  1 │ BJ   │ 2021-05-19 │
│  4 │ BJ   │ 2021-05-19 │
│  7 │ BJ   │ 2021-05-19 │
│ 10 │ BJ   │ 2021-05-19 │
└────┴──────┴────────────┘
CK內部會自動的合並分區的數據, 也會刪除多余的文件夾中的數據

4)數據存儲原理
# 合並前
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_11_11_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_2_2_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_5_5_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_8_8_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_12_12_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_3_3_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_6_6_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_9_9_0
drwxr-x---. 2 clickhouse clickhouse   6 May 19 16:48 detached
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_10_10_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_1_1_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_4_4_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_7_7_0
-rw-r-----. 1 clickhouse clickhouse   1 May 19 16:48 format_version.txt
# 合並后 
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_11_11_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:49 0b1654565b11c57ce8e06fba0d990406_2_11_1
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_2_2_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_5_5_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 0b1654565b11c57ce8e06fba0d990406_8_8_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_12_12_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:49 4ac8f272bc049477e80a3f42338ca531_3_12_1
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_3_3_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_6_6_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 4ac8f272bc049477e80a3f42338ca531_9_9_0
drwxr-x---. 2 clickhouse clickhouse   6 May 19 16:48 detached
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_10_10_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_1_1_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:49 e35d0ca9d946a627c9fc98b8f80391ce_1_10_1
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_4_4_0
drwxr-x---. 2 clickhouse clickhouse 223 May 19 16:48 e35d0ca9d946a627c9fc98b8f80391ce_7_7_0
-rw-r-----. 1 clickhouse clickhouse   1 May 19 16:48 format_version.txt
-- 進入到一個分區目錄中查看
-rw-r-----. 1 clickhouse clickhouse 385 May 19 17:12 checksums.txt
-rw-r-----. 1 clickhouse clickhouse  38 May 19 17:12 city.bin
-rw-r-----. 1 clickhouse clickhouse  48 May 19 17:12 city.mrk2
-rw-r-----. 1 clickhouse clickhouse  74 May 19 17:12 columns.txt
-rw-r-----. 1 clickhouse clickhouse   1 May 19 17:12 count.txt
-rw-r-----. 1 clickhouse clickhouse  34 May 19 17:12 ctime.bin
-rw-r-----. 1 clickhouse clickhouse  48 May 19 17:12 ctime.mrk2
-rw-r-----. 1 clickhouse clickhouse  30 May 19 17:12 id.bin
-rw-r-----. 1 clickhouse clickhouse  48 May 19 17:12 id.mrk2
-rw-r-----. 1 clickhouse clickhouse   6 May 19 17:12 minmax_city.idx
-rw-r-----. 1 clickhouse clickhouse   3 May 19 17:12 partition.dat
-rw-r-----. 1 clickhouse clickhouse   2 May 19 17:12 primary.idx

 

 

(1)partition:分區目錄,余下各類數據文件(primary.idx、 [Column].mrk、[Column].bin等)都是以分區目錄的形式被組織存放 的,屬於相同分區的數據,最終會被合並到同一個分區目錄,而不同分 區的數據,永遠不會被合並在一起。

(2)checksums.txt:校驗文件,使用二進制格式存儲。它保存了余下各類文件(primary.idx、count.txt等)的size大小及size的哈希值,用於快速校驗文件的完整性和正確性。

(3)columns.txt:列信息文件,使用明文格式存儲。用於保存此數據分區下的列字段信息,例如:

(4)count.txt:計數文件,使用明文格式存儲。用於記錄當前數 據分區目錄下數據的總行數

(5)primary.idx:一級索引文件,使用二進制格式存儲。用於存放稀疏索引,一張MergeTree表只能聲明一次一級索引(通過ORDER BY 或者PRIMARY KEY)。借助稀疏索引,在數據查詢的時能夠排除主鍵條 件范圍之外的數據文件,從而有效減少數據掃描范圍,加速查詢速度。

(6)[Column].bin:數據文件,使用壓縮格式存儲,默認為LZ4壓縮格式,用於存儲某一列的數據。由於MergeTree采用列式存儲,所以每一個列字段都擁有獨立的.bin數據文件,並以列字段名稱命名(例如 CounterID.bin、EventDate.bin等)。

(7)[Column].mrk:列字段標記文件,使用二進制格式存儲。標記文件中保存了.bin文件中數據的偏移量信息。標記文件與稀疏索引對齊,又與.bin文件一一對應,所以MergeTree通過標記文件建立了 primary.idx稀疏索引與.bin數據文件之間的映射關系。即首先通過稀疏索引(primary.idx)找到對應數據的偏移量信息(.mrk),再通過 偏移量直接從.bin文件中讀取數據。由於.mrk標記文件與.bin文件一一對應,所以MergeTree中的每個列字段都會擁有與其對應的.mrk標記文件(例如CounterID.mrk、EventDate.mrk等)。

(8)[Column].mrk2:如果使用了自適應大小的索引間隔.則標記 文件會以.mrk2命名。它的工作原理和作用與.mrk標記文件相同。

(9)partition.dat與minmax_[Column].idx:如果使用了分區鍵,例如PARTITION BY EventTime,則會額外生成partition.dat與 minmax索引文件,它們均使用二進制格式存儲。partition.dat用於保 存當前分區下分區表達式最終生成的值;而minmax索引用於記錄當前分 區下分區字段對應原始數據的最小和最大值。例如EventTime字段對應 的原始數據為2019-05-01、2019-05-05,分區表達式為PARTITION BY toYYYYMM(EventTime)。partition.dat中保存的值將會是2019-05,而 minmax索引中保存的值將會是2019-05-012019-05-05。

在這些分區索引的作用下,進行數據查詢時能夠快速跳過不必要的 數據分區目錄,從而減少最終需要掃描的數據范圍。

(10)skp_idx[Column].idx與skp_idx[Column].mrk:如果在建 表語句中聲明了二級索引,則會額外生成相應的二級索引與標記文件, 它們同樣也使用二進制存儲。二級索引在ClickHouse中又稱跳數索引, 目前擁有minmax、set、ngrambf_v1和tokenbf_v1四種類型。這些索引 的最終目標與一級稀疏索引相同,都是為了進一步減少所需掃描的數據 范圍,以加速整個查詢過程。

 

 

2.2 ReplacingMergeTree

這個引擎是在 MergeTree 的基礎上,添加了“處理重復數據”的功能,該引擎和MergeTree的不同之處在於它會刪除具有相同(區內)排序一樣的重復項。數據的去重只會在合並的過程中出現。合並會在未知的時間在后台進行,所以你無法預先作出計划。有一些數據可能仍未被處理。因此,ReplacingMergeTree 適用於在后台清除重復的數據以節省空間,但是它不保證沒有重復的數據出現

1 無版本參數

drop table if  exists test_replacingMergeTree1 ;
create table test_replacingMergeTree1(
    oid Int8 ,
    ctime DateTime ,
    cost Decimal(10,2)
)engine = ReplacingMergeTree()
order by oid 
partition by toDate(ctime) ;
​
-- 插入數據 
insert into test_replacingMergeTree1 values(3,'2021-01-01 11:11:11',30) ;
insert into test_replacingMergeTree1 values(1,'2021-01-01 11:11:14',40) ;
insert into test_replacingMergeTree1 values(1,'2021-01-01 11:11:11',10);
insert into test_replacingMergeTree1 values(2,'2021-01-01 11:11:11',20) ;
insert into test_replacingMergeTree1 values(1,'2021-01-02 11:11:11',41) ;
​
-- 優化合並
optimize table test_replacingMergeTree1 final ;
┌─oid─┬───────────────ctime─┬──cost─┐
│   1 │ 2021-01-02 11:11:11 │ 41.00 │
└─────┴─────────────────────┴───────┘
┌─oid─┬───────────────ctime─┬──cost─┐
│   1 │ 2021-01-01 11:11:11 │ 10.00 │
│   2 │ 2021-01-01 11:11:11 │ 20.00 │
│   3 │ 2021-01-01 11:11:11 │ 30.00 │
└─────┴─────────────────────┴───────┘
由於系統對CK的操作是多線程執行的, 所以不能保證數據插入的順序 , 就可能出現數據刪除錯亂的現象
-- 主鍵oid  排序字段兩個  驗證去重規則是按主鍵還是排序字段
drop table if  exists test_replacingMergeTree2 ;
create table test_replacingMergeTree2(
    oid Int8 ,
    ctime DateTime ,
    cost Decimal(10,2)
)engine = ReplacingMergeTree()
primary key oid
order by (oid ,ctime)
partition by toDate(ctime) ;
​
insert into test_replacingMergeTree2 values(1,'2021-01-01 11:11:11',10) ;
insert into test_replacingMergeTree2 values(1,'2021-01-01 11:11:11',20) ;
insert into test_replacingMergeTree2 values(1,'2021-01-01 11:11:11',30);
insert into test_replacingMergeTree2 values(1,'2021-01-01 11:11:12',40) ;
insert into test_replacingMergeTree2 values(1,'2021-01-01 11:11:13',50) ;
-- 由此可見 去重並不是根據主鍵,而知根據區內排序相同的數據會被刪除
┌─oid─┬───────────────ctime─┬──cost─┐
│   1 │ 2021-01-01 11:11:11 │ 30.00 │
│   1 │ 2021-01-01 11:11:12 │ 40.00 │
│   1 │ 2021-01-01 11:11:13 │ 50.00 │
└─────┴─────────────────────┴───────┘

  

2 有版本參數

  • 版本字段可以是數值

  • 版本字段可以是時間

drop table if  exists test_replacingMergeTree3 ;
create table test_replacingMergeTree3(
    oid Int8 ,
    ctime DateTime ,
    cost Decimal(10,2)
)engine = ReplacingMergeTree(ctime)
order by oid 
partition by toDate(ctime) ;
​
insert into test_replacingMergeTree3 values(1,'2021-01-01 11:11:11',10) ;
insert into test_replacingMergeTree3 values(1,'2021-01-01 11:11:12',20) ;
insert into test_replacingMergeTree3 values(1,'2021-01-01 11:11:10',30);
insert into test_replacingMergeTree3 values(1,'2021-01-01 11:11:19',40) ;
insert into test_replacingMergeTree3 values(1,'2021-01-01 11:11:13',50) ;
-- 合並數據以后 保留的是時間最近的一條數據
┌─oid─┬───────────────ctime─┬──cost─┐
│   1 │ 2021-01-01 11:11:19 │ 40.00 │
└─────┴─────────────────────┴───────┘

  

總結:

(1)使用ORDER BY排序鍵作為判斷重復數據的唯一依據。

(2)只有在合並分區的時候才會觸發刪除重復數據的邏輯。

(3)以數據分區為單位刪除重復數據。當分區合並時,同一分區內的重復數據會被刪除;不同分區之間的重復數據不會被刪除。

(4)在進行數據去重時,因為分區內的數據已經基於ORBER BY進行了排序,所以能夠找到那些相鄰的重復數據。

(5)數據去重策略有兩種:

  1. 如果沒有設置ver版本號,則保留同一組重復數據中的最后一行。

  2. 如果設置了ver版本號,則保留同一組重復數據中ver字段取值 最大的那一行。

2.3 CollapsingMergeTree

CollapsingMergeTree就是一種通過以增代刪的思路,支持行級數 據修改和刪除的表引擎。它通過定義一個sign標記位字段,記錄數據行 的狀態。如果sign標記為1,則表示這是一行有效的數據;如果sign標 記為-1,則表示這行數據需要被刪除。當CollapsingMergeTree分區合 並時,同一數據分區內,sign標記為1和-1的一組數據會被抵消刪除。 這種1和-1相互抵消的操作,猶如將一張瓦楞紙折疊了一般。這種直觀 的比喻,想必也正是折疊合並樹(CollapsingMergeTree)名稱的由來,

多行的排序相同的狀態為1的數據會折疊成一行

ENGINE = CollapsingMergeTree(sign)

drop table if exists tb_cps_merge_tree1 ;
CREATE TABLE tb_cps_merge_tree1
(
    user_id UInt64,
    name String,
    age UInt8,
    sign Int8
)
ENGINE = CollapsingMergeTree(sign)
ORDER BY user_id;
-- 插入數據 
insert into tb_cps_merge_tree1 values(1,'xiaoluo',23,1),(2,'xiaoyu',24,1),(3,'xiaofeng',25,1) ;
insert into tb_cps_merge_tree1 values(1,'xiaoluo_',23,-1),(2,'xiaoyu_',24,-1),(3,'xiaofeng2',25,1) ;
-- 合並優化
optimize table tb_cps_merge_tree1 ;
-- 實現了數據的刪除和已經存在數據的更新
SELECT *
FROM tb_cps_merge_tree1
┌─user_id─┬─name──────┬─age─┬─sign─┐
│       3 │ xiaofeng2 │  25 │    1 │
└─────────┴───────────┴─────┴──────┘
​
CollapsingMergeTree雖然解決了主鍵相同的數據即時刪除的問題,但是狀態持續變化且多線程並行寫入情況下,狀態行與取消行位置可能亂序,導致無法正常折疊。只有保證老的狀態行在在取消行的上面, 新的狀態行在取消行的下面! 但是多線程無法保證寫的順序!
​
drop table if exists tb_cps_merge_tree2 ;
CREATE TABLE tb_cps_merge_tree2
(
    user_id UInt64,
    name String,
    age UInt8,
    sign Int8
)
ENGINE = CollapsingMergeTree(sign)
ORDER BY user_id;
insert into tb_cps_merge_tree2 values(1,'xiaoluo_',23,-1),(2,'xiaoyu_',24,-1),(3,'xiaofeng2',25,1) ;
insert into tb_cps_merge_tree2 values(1,'xiaoluo',23,1),(2,'xiaoyu',24,1),(3,'xiaofeng',25,1) ;
-- 合並優化
optimize table tb_cps_merge_tree2 ;
┌─user_id─┬─name─────┬─age─┬─sign─┐
│       1 │ xiaoluo_ │  23 │   -1 │
│       1 │ xiaoluo  │  23 │    1 │
│       2 │ xiaoyu_  │  24 │   -1 │
│       2 │ xiaoyu   │  24 │    1 │
│       3 │ xiaofeng │  25 │    1 │
└─────────┴──────────┴─────┴──────┘
假如有相同的排序數據,並且狀態都是1,可以實現數據的更新 ,如果我們不能保證折疊的行在狀態行的下面 ,數據無法保證可以正常刪除
----查詢正確的數據 
select 
tb_cps_merge_tree2.*
from
tb_cps_merge_tree2
join
(
select 
user_id ,
sum(sign) as sum_sign
from 
tb_cps_merge_tree2
group by user_id
having sum_sign = 1
 )t
 on tb_cps_merge_tree2.user_id =t.user_id ;
 
 ┌─user_id─┬─name─────┬─age─┬─sign─┐
│       3 │ xiaofeng │  25 │    1 │
└─────────┴──────────┴─────┴──────┘

  

2.4 VersionedCollapsingMergeTree

為了解決CollapsingMergeTree亂序寫入情況下無法正常折疊(刪除)問題,VersionedCollapsingMergeTree表引擎在建表語句中新增了一列Version,用於在亂序情況下記錄狀態行與取消行的對應關系。主鍵(排序)相同,且Version相同、Sign相反的行,在Compaction時會被刪除。與CollapsingMergeTree類似, 為了獲得正確結果,業務層需要改寫SQL,將count()、sum(col)分別改寫為sum(Sign)、sum(col * Sign)。

drop table if exists tb_vscmt ;
CREATE TABLE tb_vscmt
(
    uid UInt64,
    name String,
    age UInt8,
    sign Int8,
    version UInt8
)
ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY uid;
​
INSERT INTO tb_vscmt VALUES (1001, 'ADA', 18, -1, 1);
INSERT INTO tb_vscmt VALUES (1001, 'ADA', 18, 1, 1),(101, 'DAD', 19, 1, 1),(101, 'DAD', 11, 1, 3); 
INSERT INTO tb_vscmt VALUES(101, 'DAD', 11, 1, 2) ;
-- 可以保證要刪除的數據會被刪除, 沒有折疊標記的數據會被保留
optimize table tb_vscmt ; 
┌─uid─┬─name─┬─age─┬─sign─┬─version─┐
│ 101 │ DAD  │  19 │    1 │       1 │
│ 101 │ DAD  │  11 │    1 │       2 │
│ 101 │ DAD  │  11 │    1 │       3 │
└─────┴──────┴─────┴──────┴─────────┘
版本不一致的數據不會被折疊刪除
┌──uid─┬─name─┬─age─┬─sign─┬─version─┐
│  101 │ DAD  │  19 │    1 │       1 │
│  101 │ DAD  │  11 │    1 │       2 │
│  101 │ DAD  │  11 │    1 │       3 │
│ 1001 │ ADA  │  18 │   -1 │       1 │
│ 1001 │ ADA  │  18 │    1 │       2 │
└──────┴──────┴─────┴──────┴─────────┘

  

2.5 SummingMergeTree

假設有這樣一種查詢需求:終端用戶只需要查詢數據的匯總結果,不關心明細數據,並且數據的匯總條件是預先明確的(GROUP BY 條件明確,且不會隨意改變)。

對於這樣的查詢場景,在ClickHouse中如何解決呢?最直接的方 案就是使用MergeTree存儲數據,然后通過GROUP BY聚合查詢,並利用 SUM聚合函數匯總結果。這種方案存在兩個問題。

  1. 存在額外的存儲開銷:終端用戶不會查詢任何明細數據,只關心匯總結果,所以不應該一直保存所有的明細數據。

  2. 存在額外的查詢開銷:終端用戶只關心匯總結果,雖然 MergeTree性能強大,但是每次查詢都進行實時聚合計算也是一種性能消耗。

SummingMergeTree就是為了應對這類查詢場景而生的。顧名思義,它能夠在合並分區的時候按照預先定義的條件聚合匯總數據,將同一分組下的多行數據匯總合並成一行,這樣既減少了數據行,又降低了后續匯總查詢的開銷。

提示:

ORDER BY (A、B、C、D) 
PRIMARY KEY A 
這種強制約束保障了即便在兩者定義不同的情況下,主鍵仍然是排序鍵的前綴,不會出現索引與數據順序混亂的問題。
在定義表的主鍵的時候,我們會考慮主鍵上的索引快速查找數據
ORDER BY (B、C) PRIMARY KEY A   這種是錯誤的!

  

drop table summing_table ;
CREATE TABLE summing_table( 
id String, 
city String, 
sal UInt32, 
comm Float64, 
ctime DateTime 
)ENGINE = SummingMergeTree() 
PARTITION BY toDate(ctime) 
ORDER BY (id, city) 
PRIMARY KEY id ;
-- 插入素具 
insert into summing_table
values 
(1,'shanghai',10,20,'2021-06-12 01:11:12'),
(1,'shanghai',20,30,'2021-06-12 01:11:12'),
(3,'shanghai',10,20,'2021-11-12 01:11:12'),
(3,'Beijing',10,20,'2021-11-12 01:11:12') ;
​
optimize table summing_table ;
​
┌─id─┬─city─────┬─sal─┬─comm─┬───────────────ctime─┐
│ 3  │ Beijing  │  10 │   20 │ 2021-11-12 01:11:12 │
│ 3  │ shanghai │  10 │   20 │ 2021-11-12 01:11:12 │
└────┴──────────┴─────┴──────┴─────────────────────┘
┌─id─┬─city─────┬─sal─┬─comm─┬───────────────ctime─┐
│ 1  │ shanghai │  30 │   50 │ 2021-06-12 01:11:12 │
└────┴──────────┴─────┴──────┴─────────────────────┘

  

上面的例子中沒有指定sum的字段 ,那么表中符合要求的所有的數值字段都會進行求和 ,我們可以在建表的時候執行求和的字段

drop table summing_table2 ;
CREATE TABLE summing_table2( 
id String, 
city String, 
money UInt32, 
num UInt32, 
ctime DateTime 
)ENGINE = SummingMergeTree(money) 
PARTITION BY toDate(ctime) 
ORDER BY city ;
--每個城市每天的銷售總額 
insert into summing_table2 values(1,'BJ',100,11,now()),
(2,'BJ',100,11,now()),
(3,'BJ',100,11,now()),
(4,'NJ',100,11,now()),
(5,'NJ',100,11,now()),
(6,'SH',100,11,now()),
(7,'BJ',100,11,'2021-05-18 11:11:11'),
(8,'BJ',100,11,'2021-05-18 11:11:11') ;
​
SELECT * 
FROM summing_table2 ;
┌─id─┬─city─┬─money─┬─num─┬───────────────ctime─┐
│ 1  │ BJ   │   300 │  11 │ 2021-05-19 21:53:49 │
│ 4  │ NJ   │   200 │  11 │ 2021-05-19 21:53:49 │
│ 6  │ SH   │   100 │  11 │ 2021-05-19 21:53:49 │
└────┴──────┴───────┴─────┴─────────────────────┘
┌─id─┬─city─┬─money─┬─num─┬───────────────ctime─┐
│ 7  │ BJ   │   200 │  11 │ 2021-05-18 11:11:11 │
└────┴──────┴───────┴─────┴─────────────────────┘
SELECT city ,money
FROM summing_table2 ;
┌─city─┬─money─┐
│ BJ   │   300 │
│ NJ   │   200 │
│ SH   │   100 │
└──────┴───────┘
┌─city─┬─money─┐
│ BJ   │   200 │
└──────┴───────┘

支持嵌套格式的求和操作

CREATE TABLE summing_table_nested( 
id String, 
nestMap Nested( 
id UInt32, 
key UInt32, 
val UInt64 
), 
create_time DateTime 
)ENGINE = SummingMergeTree() 
PARTITION BY toYYYYMM(create_time) 
ORDER BY id ; 

  

總結:

(1)用ORBER BY排序鍵作為聚合數據的條件Key。

(2)只有在合並分區的時候才會觸發匯總的邏輯。

(3)以數據分區為單位來聚合數據。當分區合並時,同一數據分區內聚合Key相同的數據會被合並匯總,而不同分區之間的數據則不會被匯總。

(4)如果在定義引擎時指定了columns匯總列(非主鍵的數值類 型字段),則SUM匯總這些列字段;如果未指定,則聚合所有非主鍵的數值類型字段。

(5)在進行數據匯總時,因為分區內的數據已經基於ORBER BY排序,所以能夠找到相鄰且擁有相同聚合Key的數據。

(6)在匯總數據時,同一分區內,相同聚合Key的多行數據會合並成一行。其中,匯總字段會進行SUM計算;對於那些非匯總字段,則會使用第一行數據的取值。

(7)支持嵌套結構,但列字段名稱必須以Map后綴結尾。嵌套類 型中,默認以第一個字段作為聚合Key。除第一個字段以外,任何名稱 以Key、Id或Type為后綴結尾的字段,都將和第一個字段一起組成復合 Key。

2.5 AggregatingMergeTree

AggregatingMergeTree就有些許數據立方體的意思,它能夠在合並分區的時候,按照預先定義的條件聚合數據。同時,根據預先定義的聚合函數計算數據並通過二進制的格式存入表內。將同一分組下的多行數據聚合成一行,既減少了數據行,又降低了后續聚合查詢的開銷。可以說,AggregatingMergeTree 是SummingMergeTree的升級版,它們的許多設計思路是一致的,例如同時定義 ORDER BY與PRIMARY KEY的原因和目的。但是在使用方法上,兩者存在明顯差異,應該說AggregatingMergeTree的定義方式是MergeTree家族中最為特殊的一個。

NGINE = AggregatingMergeTree() 

  

AggregatingMergeTree沒有任何額外的設置參數,在分區合並時,在每個數據分區內,會按照ORDER BY聚合。而使用何種聚合函數,以及針對哪些列字 段計算,則是通過定義AggregateFunction數據類型實現的。在insert和select時,也有獨特的寫法和要求:寫入時需要使用-State語法,查詢時使用-Merge語法。

AggregateFunction(arg1 , arg2) ;

參數一 聚合函數

參數二 數據類型

sum_cnt AggregateFunction(sum, Int64) ;

先創建原始表 ---插入數據---> 創建預先聚合表 --通過Insert的方式導入數據, 數據會按照指定的聚合函數聚合預先數據!

 

 

 

-- 1)建立明細表
CREATE TABLE detail_table
(id UInt8,
 ctime Date,
 money UInt64
) ENGINE = MergeTree() 
PARTITION BY toDate(ctime) 
ORDER BY id;

-- 2)插入明細數據
INSERT INTO detail_table VALUES(1, '2021-08-06', 100);
INSERT INTO detail_table VALUES(1, '2021-08-06', 100);
INSERT INTO detail_table VALUES(2, '2021-08-07', 200);
INSERT INTO detail_table VALUES(2, '2021-08-07', 200);

-- 3)建立預先聚合表,
-- 注意:其中UserID一列的類型為:AggregateFunction(uniq, UInt64)
CREATE TABLE agg_table
(id UInt8,
ctime Date,
money AggregateFunction(sum, UInt64)
) ENGINE = AggregatingMergeTree() 
PARTITION BY  toDate(ctime) 
ORDER BY id;

-- 4) 從明細表中讀取數據,插入聚合表。
-- 注意:子查詢中使用的聚合函數為 uniqState, 對應於寫入語法<agg>-State
INSERT INTO agg_table
select id, ctime, uniqState(money)
from detail_table
group by id, ctime ;

-- 不能使用普通insert語句向AggregatingMergeTree中插入數據。
-- 本SQL會報錯:Cannot convert UInt64 to AggregateFunction(uniq, UInt64)
INSERT INTO agg_table VALUES(1, '2020-08-06', 1);

-- 5) 從聚合表中查詢。
-- 注意:select中使用的聚合函數為uniqMerge,對應於查詢語法<agg>-Merge
SELECT
id, ctime ,
uniqMerge(uid) AS state 
FROM agg_table 
GROUP BY id, ctime;

使用物化視圖同步聚合數據

-- 建立明細表
CREATE TABLE orders
(
    uid UInt64,
    money UInt64,
    ctime Date,
    Sign Int8
)
ENGINE = CollapsingMergeTree(Sign)
ORDER BY uid;
​
--插入數據 
insert into orders values(1,100,toDate(now()),1) ;
insert into orders values(1,100,toDate(now()),1) ;
insert into orders values(1,100,toDate(now()),1) ;
insert into orders values(2,200,toDate(now()),1) ;
insert into orders values(2,200,toDate(now()),1) ;
insert into orders values(2,200,toDate(now()),1) ;
-- 將聚合邏輯創建成物化視圖
CREATE MATERIALIZED VIEW orders_agg_view
ENGINE = AggregatingMergeTree() 
PARTITION BY toDate(ctime)
ORDER BY uid 
populate
as select 
uid ,
ctime ,
sumState(money) as mm  -- 注意別名 
from 
orders
group by uid , ctime;
​
-- 查詢物化視圖數據
select uid,ctime,sumMerge(mm) from orders_agg_view group by uid, ctime ;
​
-- 更新明細數據, 物化視圖中的數據實時計算更新
insert into orders values(1,100,toDate(now()),1);
┌─uid─┬──────ctime─┬─sumMerge(mm)─┐
│   2 │ 2021-05-19 │          400 │
│   1 │ 2021-05-19 │          200 │
└─────┴────────────┴──────────────┘
┌─uid─┬──────ctime─┬─sumMerge(mm)─┐
│   2 │ 2021-05-19 │          400 │
│   1 │ 2021-05-19 │          300 │
└─────┴────────────┴──────────────┘
┌─uid─┬──────ctime─┬─sumMerge(mm)─┐
│   2 │ 2021-05-19 │          400 │
│   1 │ 2021-05-19 │          400 │
└─────┴────────────┴──────────────┘

  

總結:

(1)用ORBER BY排序鍵作為聚合數據的條件Key。

(2)使用AggregateFunction字段類型定義聚合函數的類型以及聚合的字 段。

(3)只有在合並分區的時候才會觸發聚合計算的邏輯。

(4)以數據分區為單位來聚合數據。當分區合並時,同一數據分區內聚合 Key相同的數據會被合並計算,而不同分區之間的數據則不會被計算。

(5)在進行數據計算時,因為分區內的數據已經基於ORBER BY排序,所以 能夠找到那些相鄰且擁有相同聚合Key的數據。

(6)在聚合數據時,同一分區內,相同聚合Key的多行數據會合並成一 行。對於那些非主鍵、非AggregateFunction類型字段,則會使用第一行數據的 取值。

(7)AggregateFunction類型的字段使用二進制存儲,在寫入數據時,需 要調用State函數;而在查詢數據時,則需要調用相應的Merge函數。其中,* 表示定義時使用的聚合函數。

(8)AggregatingMergeTree通常作為物化視圖的表引擎,與普通 MergeTree搭配使用。

 

該查詢嘗試使用[MergeTree]系列中的表引擎初始化表的未計划的數據部分合並。[MaterializedView和[Buffer]引擎OPTMIZE也支持。不支持其他表引擎。

當OPTIMIZE與使用[ReplicatedMergeTree]表引擎,ClickHouse創造了合並,並等待所有節點上執行(如果該任務replication_alter_partitions_sync已啟用設置)。

· 如果OPTIMIZE由於任何原因未執行合並,則不會通知客戶端。要啟用通知,請使用[optimize_throw_if_noop]設置。

· 如果指定PARTITION,則僅優化指定的分區。[如何設置分區表達式]。

· 如果指定FINAL,即使所有數據已經在一個部分中,也會執行優化。

· 如果指定DEDUPLICATE,則將對完全相同的行進行重復數據刪除(比較所有列),這僅對MergeTree引擎有意義。

 

3 外部存儲引擎

3.1 HDFS引擎

Clickhouse可以直接從HDFS中指定的目錄下加載數據 , 自己根本不存儲數據, 僅僅是讀取數據

ENGINE = HDFS(hdfs_uri,format) 
·hdfs_uri表示HDFS的文件存儲路徑; 
·format表示文件格式(指ClickHouse支持的文件格式,常見的有 CSV、TSV和JSON等)。

我們一般期望的是數據有其他方式寫入到HDFS系統中, 使用CK的HDFS引擎加載處理分析數據.

這種形式類似Hive的外掛表,由其他系統直接將文件寫入HDFS。通過HDFS表引擎的hdfs_uri和format參數分別與HDFS的文件路徑、文件格式建立映射。其中,hdfs_uri支持以下幾種常見的配置方法:

  • 絕對路徑:會讀取指定路徑的單個文件,例如/clickhouse/hdfs_table1。

  • 通配符:匹配所有字符,例如路徑為/clickhouse/hdfs_table/,則會讀取/click-house/hdfs_table路徑下的所有文件。

  • ?通配符:匹配單個字符,例如路徑為/clickhouse/hdfs_table/organization?.csv,則會讀取/clickhouse/hdfs_table路徑下與organization?.csv匹配的文件,其中?代表任意一個合法字符。

  • {M..N}數字區間:匹配指定數字的文件,例如路徑為/clickhouse/hdfs_table/organization_{1..3}.csv,則會讀取/clickhouse/hdfs_table/路徑下的文件organization_1.csv、organization_2.csv和organization_3.csv。

create table test_hdfs1(
id Int8 ,
name String ,
age Int8
)engine=HDFS('hdfs://linux01:8020/ck/test1/*' ,CSV) ;
​
創建文件,將文件上傳到指定的目錄下
1.txt
1,zss,21
2,ww,22
​
2.txt 
3,lss,33
4,qaa,32
​
3.txt 
5,as,31
6,ghf,45
​
--匹配單個字符 
create table test_hdfs2(
id Int8 ,
name String ,
age Int8
)engine=HDFS('hdfs://linux01:8020/ck/test1/?.txt' ,CSV) ;
​
-- 匹配數字之間的文件
create table test_hdfs3(
id Int8 ,
name String ,
age Int8
)engine=HDFS('hdfs://linux01:8020/ck/test1/a_{1..2}.txt' ,CSV) ;

  

3.2 mysql引擎

MySQL表引擎可以與MySQL數據庫中的數據表建立映射,並通過SQL向其發起遠程查詢, 包括SELECT和INSERT

它的聲明方式如下:

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
    ...
) ENGINE = MySQL('host:port', 'database', 'table', 'user', 'password'[, replace_query, 'on_duplicate_clause']);

  

其中各參數的含義分別如下:

·host:port表示MySQL的地址和端口。

·database表示數據庫的名稱。

·table表示需要映射的表名稱。

·user表示MySQL的用戶名。

·password表示MySQL的密碼。

·replace_query默認為0,對應MySQL的REPLACE INTO語法。如果將它設置為1,則會用REPLACE INTO代替INSERT INTO。

·on_duplicate_clause默認為0,對應MySQL的ON DUPLICATE KEY語法。如果需要使用該設置,則必須將replace_query設置成0。

那么在正式使用MySQL引擎之前首先當前機器要有操作MySQL數據的權限 ,開放MySQL的遠程連連接權限操作如下:

1)  set global validate_password_policy=0;
2)  set global validate_password_length=1;   這個兩個設置以后 密碼很簡單不會報錯
3)  grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
4)  flush privileges;

  

-- 在mysql中建表 
create table tb_x(id int, name varchar(25), age int) ;
insert into tb_x values(1,'zss',23),(2,'lss',33) ;
-- 在clickhouse中建表 
CREATE TABLE tb_mysql
(
    `id` Int8,
    `name` String,
    `age` Int8
)
ENGINE = MySQL('doit01:3306', 'test1', 'tb_x', 'root', 'root');
-- 查看數據
-- 插入數據
insert into tb_mysql values(3,'ww',44) ;
  • 支持查詢數據

  • 支持插入數據

  • 不支持刪除和更新操作

3.3 File引擎

File表引擎能夠直接讀取本地文件的數據,通常被作為一種擴充手段來使用。例如:它可以讀取由其他系統生成的數據文件,如果外部系統直接修改了文件,則變相達到了數據更新的目的;它可以將 ClickHouse數據導出為本地文件;它還可以用於數據格式轉換等場景。除此以外,File表引擎也被應用於clickhouse-local工具

ENGINE = File(format)

  

drop table if exists test_file1 ;
create table test_file1(
id String ,
name String ,
age UInt8
)engine=File("CSV") ;

  

在默認的目錄下回生成一個文件夾 , 文件夾中可以寫入文件 ,但是文件的名字必須是data.CSV

insert into  test_file1 values('u001','hangge',33) ; 

  

file表函數

-- 去指定的路徑下加載本地的數據

select * from file('/ck/user.txt','CSV','id Int8 , name String ,gender String,age UInt8') ;

默認加載的是特定的文件夾,數據一定要在指定的文件夾下才會被加載

修改默認的數據加載的文件夾

vi /etc/clickhouse-server/config.xml 
/path  n下一個 
    <!-- Directory with user provided files that are accessible by 'file' table function. -->
    <user_files_path>/</user_files_path>

重啟服務

service clickhouse-server restart 

  

 

3.4 MySQL 數據庫引擎

語法

CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')

  

create database db_ck_mysql engine=MySQL('doit01:3306','test1','root','root') ;

  

常用於數據的合並 , 加載mysql中的數據和ck中的數據合並 , 不做數據的修改和建表

4 內存引擎

接下來將要介紹的幾款表引擎,都是面向內存查詢的,數據會從內存中被直接訪問,所以它們被歸納為內存類型。但這並不意味着內存類表引擎不支持物理存儲,事實上,除了Memory表引擎之外,其余的幾款表引擎都會將數據寫入磁盤,這是為了防止數據丟失,是一種故障恢復手段。而在數據表被加載時,它們會將數據全部加載至內存,以供查詢之用。將數據全量放在內存中,對於表引擎來說是一把雙刃劍:一方面,這意味着擁有較好的查詢性能;而另一方面,如果表內裝載的數據量過大,可能會帶來極大的內存消耗和負擔!

4.1 Memory

Memory表引擎直接將數據保存在內存中,數據既不會被壓縮也不會被格式轉換,數據在內存中保存的形態與查詢時看到的如出一轍。 正因為如此,當ClickHouse服務重啟的時候,Memory表內的數據會全部丟失。所以在一些場合,會將Memory作為測試表使用,很多初學者在學習ClickHouse的時候所寫的Hello World程序很可能用的就是Memory表。由於不需要磁盤讀取、序列化以及反序列等操作,所以Memory表引擎支持並行查詢,並且在簡單的查詢場景中能夠達到與MergeTree旗鼓相當的查詢性能(一億行數據量以內)。Memory表的創建方法如下所示:

CREATE TABLE memory_1 ( 
id UInt64 
)ENGINE = Memory() ;

  

Memory表更為廣 泛的應用場景是在ClickHouse的內部,它會作為集群間分發數據的存儲載體來使用。例如在分布式IN查詢的場合中,會利用Memory臨時表保存IN子句的查詢結果,並通過網絡將它傳輸到遠端節點。

4.2 Set

Set表引擎是擁有物理存儲的,數據首先會被寫至內存,然后被同步到磁盤文件中。所以當服務重啟時,它的數據不會丟失,當數據表被重新裝載時,文件數據會再次被全量加載至內存。眾所周知,在Set

數據結構中,所有元素都是唯一的。Set表引擎具有去重的能力,在數據寫入的過程中,重復的數據會被自動忽略。然而Set表引擎的使用場景既特殊又有限,它雖然支持正常的INSERT寫入,但並不能直接使用SELECT對其進行查詢,Set表引擎只能間接作為IN查詢的右側條件被查詢使用

Set表引擎的存儲結構由兩部分組成,它們分別是:

[num].bin數據文件:保存了所有列字段的數據。其中,num是 一個自增id,從1開始。伴隨着每一批數據的寫入(每一次INSERT),都會生成一個新的.bin文件,num也會隨之加1。

tmp臨時目錄:數據文件首先會被寫到這個目錄,當一批數據寫入完畢之后,數據文件會被移出此目錄。

create table test_set(
id Int8 ,
name String
)engine=Set();

發現在數據庫的目錄下是還有對應的目錄的,可見數據會被存儲到磁盤上的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM