1. 概述
ClickHouse是一個用於聯機分析(OLAP:Online Analytical Processing)的列式數據庫管理系統(DBMS:Database Management System),簡稱CK。
ClickHouse是一個完全的列式數據庫管理系統,允許在運行時創建表和數據庫,加載數據和運行查詢,而無需重新配置和重新啟動服務器,支持線性擴展,簡單方便,高可靠性,容錯。
ClickHouse官方文檔:https://clickhouse.yandex/docs/en/
2. 應用場景
OLAP場景關鍵特征:
- 大多數是讀請求
- 數據總是以相當大的批(> 1000 rows)進行寫入
- 不修改已添加的數據
- 每次查詢都從數據庫中讀取大量的行,但是同時又僅需要少量的列
- 寬表,即每個表包含着大量的列
- 較少的查詢(通常每台服務器每秒數百個查詢或更少)
- 對於簡單查詢,允許延遲大約50毫秒
- 列中的數據相對較小: 數字和短字符串(例如,每個UR60個字節)
- 處理單個查詢時需要高吞吐量(每個服務器每秒高達數十億行)
- 事務不是必須的
- 對數據一致性要求低
- 每一個查詢除了一個大表外都很小
- 查詢結果明顯小於源數據,換句話說,數據被過濾或聚合后能夠被盛放在單台服務器的內存中
應用場景:
用於結構良好清晰且不可變的事件或日志流分析。
不適合的場景:
事務性工作(OLTP),高請求率的鍵值訪問,低延遲的修改或刪除已存在數據,Blob或文檔存儲,超標准化數據。
3. 數據類型
- 整型
固定長度的整型,包括有符號整型或無符號整型。
整型范圍:
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 : 2147483647]
Int64 - [-9223372036854775808 : 9223372036854775807]
無符號整型范圍:
UInt8 - [0 : 255] :可以使用 UInt8 類型,取值限制為0或1作為Boolean型
UInt16 - [0 : 65535]
UInt32 - [0 : 4294967295]
UInt64 - [0 : 18446744073709551615]
- 浮點型
建議盡可能以整數形式存儲數據。
Float32 - float
Float64 - double
Decimal32(S) - [ -1 * 10^(9 - S) : 1 * 10^(9 - S) ]
Decimal64(S) - [ -1 * 10^(18 - S) : 1 * 10^(18 - S) ]
Decimal128(S) - [ -1 * 10^(38 - S) : 1 * 10^(38 - S) ]
Decimal(P, S)
P - 精度。有效范圍:[1:38],決定可以有多少個十進制數字(包括分數)。
S - 規模。有效范圍:[0:P],決定數字的小數部分中包含的小數位數。
- 字符串型
文本格式建議使用UTF-8 編碼。
String:任意長度
FixedString(N):固定長度N的字符串,N必須是嚴格的正自然數。
UUID:默認值00000000-0000-0000-0000-000000000000
- 日期型
Date:最小值為0000-00-00
DateTime:最小值為 0000-00-00 00:00:00
- 枚舉型
Enum8:用 'String'= Int8 對描述。
Enum16:用 'String'= Int16 對描述。
- 數組型
Array(T):由T類型元素組成的數組,T可以是任意類型,包含數組類型,但不推薦使用多維數組。
- 元祖型
Tuple(T1, T2, ...):不能在表中存儲元組(除了內存表),它們可以用於臨時列分組。
- 其他
Nullable(TypeName):許用特殊標記 (NULL) 表示"缺失值",可以與 TypeName 的正常值存放一起。例如,Nullable(Int8) 類型的列可以存儲 Int8 類型值,而沒有值的行將存儲 NULL。
Nested(Name1 Type1, Name2 Type2, ...):嵌套數據結構類似於嵌套表。嵌套數據結構的參數(列名和類型)與 CREATE 查詢類似。每個表可以包含任意多行嵌套數據結構。
IPv4:如116.253.40.133
IPv6:如2a02:aa08:e000:3100::2
4. 表引擎
表引擎(即表的類型)決定了:
- 數據的存儲方式和位置,寫到哪里以及從哪里讀取數據
- 支持哪些查詢以及如何支持。
- 並發數據訪問。
- 索引的使用(如果存在)。
- 是否可以執行多線程請求。
- 數據復制參數。
在讀取時,引擎只需要輸出所請求的列,但在某些情況下,引擎可以在響應請求時部分處理數據。對於大多數正式的任務,應該使用MergeTree族中的引擎。
4.1. MergeTree Family
MergeTree(合並樹)引擎及其家族(*MergeTree)的其他引擎是ClickHouse健壯性最強的表引擎。
主要特點:
- 存儲的數據按主鍵排序。允許創建一個小的稀疏索引來幫助快速查詢數據。
- 允許在指定分區鍵時使用分區。
- 支持數據復制。
- 支持數據采樣。
4.1.1. MergeTree
建表語句:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1, INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2 ) ENGINE = MergeTree() [PARTITION BY expr] [ORDER BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- ON CLUSTER cluster:指定分片,可用於建分布式表。
- ENGINE - 引擎名和參數(必填)。
- PARTITION BY — 分區鍵(可選)。
參數為Date類型,默認要天分區;要按月分區,可以使用表達式toYYYYMM(date_column)
- ORDER BY — 表的排序鍵(可選)。
可以是一組列的元組或任意的表達式。例如: ORDER BY (CounterID, EventDate) 。
- PRIMARY KEY - 主鍵(可選),如果要設成跟排序鍵不相同。
通常主鍵默認和排序(ORDER BY)字段相同,不需另外指定。
- SAMPLE BY — 用於抽樣的表達式(可選)。
如果要用抽樣表達式,主鍵中必須包含這個表達式。例如: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)) 。
- SETTINGS — 影響 MergeTree 性能的額外參數(可選):
控制合並樹的其他參數設置(可選)。index_granularity——索引的粒度。索引“標記”之間的數據行數。默認值:8192。
新版CLickHouse不建議使用以下方式建表:
ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity)
4.1.2. ReplacingMergeTree
該引擎和MergeTree的不同之處在於它會刪除具有相同主鍵的重復項。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- ver — 版本列。類型為 UInt*, Date 或 DateTime。可選參數。
合並的時候,ReplacingMergeTree 從所有具有相同主鍵的行中選擇一行留下:如果 ver 列未指定,選擇最后一條。如果 ver 列已指定,選擇 ver 值最大的版本。
4.1.3. SummingMergeTree
該引擎繼承自 MergeTree。區別在於,當合並SummingMergeTree表的數據片段時,ClickHouse 會把所有具有相同主鍵的行合並為一行,該行包含了被合並的行中具有數值數據類型的列的匯總值。如果主鍵的組合方式使得單個鍵值對應於大量的行,則可以顯著的減少存儲空間並加快數據查詢的速度。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = SummingMergeTree([columns]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- columns - 包含了將要被匯總的列的列名的元組。可選參數。 所選的列必須是數值類型,並且不可位於主鍵中。
如果沒有指定 columns,ClickHouse 會把所有不在主鍵中的數值類型的列都進行匯總。
4.1.4. AggregatingMergeTree
該引擎繼承自 MergeTree,並改變了數據片段的合並邏輯。 ClickHouse 會將相同主鍵的所有行(在一個數據片段內)替換為單個存儲一系列聚合函數狀態的行。
可以使用 AggregatingMergeTree 表來做增量數據統計聚合,包括物化視圖的數據聚合。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = AggregatingMergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
4.1.5. CollapsingMergeTree
該引擎繼承於 MergeTree,並在數據塊合並算法中添加了折疊行的邏輯。
CollapsingMergeTree 會異步的刪除(折疊)這些除了特定列 Sign 有 1 和 -1 的值以外,其余所有字段的值都相等的成對的行。沒有成對的行會被保留。
該引擎可以顯著的降低存儲量並提高 SELECT 查詢效率。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = CollapsingMergeTree(sign) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- sign—類型列的名稱:1是“狀態”行,-1是“取消”行。
列數據類型 — Int8。
4.1.6. VersionedCollapsingMergeTree
該引擎繼承了MergeTree,並將折疊行的邏輯添加到合並數據部分的算法中。版本化的collapsingmergetree與collapsingmergetree具有相同的用途,但使用了不同的折疊算法,允許在多個線程中以任意順序插入數據。尤其是,“版本”列有助於正確折疊行,即使它們是按錯誤的順序插入的。相反,collapsingmergetree只允許嚴格連續插入。
允許快速寫入不斷變化的對象狀態。
刪除背景中的舊對象狀態。這大大減少了存儲容量。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = VersionedCollapsingMergeTree(sign, version) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- sign—類型的列的名稱:1是“狀態”行,-1是“取消”行。
列數據類型應為Int8
- version—具有對象狀態版本的列的名稱。
列數據類型應為Uint*。
4.1.7. GraphiteMergeTree
該引擎繼承於 MergeTree,用於細化和聚合/平均(匯總)Graphite Data。
如果不需要匯總,您可以使用任何Clickhouse表引擎來存儲Graphite Data,但是如果需要匯總,請使用GraphiteMergeTree。該引擎減少了存儲容量,並提高了Graphite Data的效率。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( Path String, Time DateTime, Value <Numeric_type>, Version <Numeric_type> ... ) ENGINE = GraphiteMergeTree(config_section) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...]
- config_section—配置文件中配置項的名稱,
4.1.8. 數據副本
只有 MergeTree 系列里的表可支持副本:
- ReplicatedMergeTree
- ReplicatedSummingMergeTree
- ReplicatedReplacingMergeTree
- ReplicatedAggregatingMergeTree
- ReplicatedCollapsingMergeTree
- ReplicatedVersionedCollapsingMergeTree
- ReplicatedGraphiteMergeTree
副本是表級別的,不是整個服務器級的。所以,服務器里可以同時有復制表和非復制表。
副本不依賴分片。每個分片有它自己的獨立副本。
4.2. Log Family
這些引擎是為了需要寫入許多小數據量(少於一百萬行)的表的場景而開發的。
這系列的引擎有:
主要特點:
- 數據存儲在磁盤上。
- 寫入時將數據追加在文件末尾。
- 不支持突變操作。
- 不支持索引。意味着 SELECT 在范圍查詢時效率不高。
- 非原子地寫入數據。
如果某些事情破壞了寫操作,例如服務器的異常關閉,你將會得到一張包含了損壞數據的表。
4.2.1. StripeLog
在你需要寫入許多小數據量(小於一百萬行)的表的場景下使用這個引擎。
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
4.2.2. TinyLog
最簡單的表引擎,用於將數據存儲在磁盤上。每列都存儲在單獨的壓縮文件中。寫入時,數據將附加到文件末尾。
並發數據訪問不受任何限制:如果同時從表中讀取並在不同的查詢中寫入,則讀取操作將拋出異常,如果同時寫入多個查詢中的表,則數據將被破壞。
這種表引擎的典型用法是 write-once:首先只寫入一次數據,然后根據需要多次讀取。查詢在單個流中執行。換句話說,此引擎適用於相對較小的表(建議最多1,000,000行)。如果您有許多小表,則使用此表引擎是適合的,因為它比Log引擎更簡單(需要打開的文件更少)。當您擁有大量小表時,可能會導致性能低下,但在可能已經在其它 DBMS 時使用過,則您可能會發現切換使用 TinyLog 類型的表更容易。不支持索引。
4.2.3. Log
Log與TinyLog的不同之處在於,"標記" 的小文件與列文件存在一起。這些標記寫在每個數據塊上,並且包含偏移量,這些偏移量指示從哪里開始讀取文件以便跳過指定的行數。這使得可以在多個線程中讀取表數據。對於並發數據訪問,可以同時執行讀取操作,而寫入操作則阻塞讀取和其它寫入。Log 引擎不支持索引。同樣,如果寫入表失敗,則該表將被破壞,並且從該表讀取將返回錯誤。Log 引擎適用於臨時數據,write-once 表以及測試或演示目的。
4.3. Distributed
分布式引擎本身不存儲數據, 但可以在多個服務器上進行分布式查詢。讀是自動並行的。讀取時,遠程服務器表的索引(如果有的話)會被使用。分布式引擎參數:服務器配置文件中的集群名,遠程數據庫名,遠程表名,數據分片鍵(可選)。
示例:
Distributed(logs, default, hits[, sharding_key])
將會從位於“logs”集群中 default.hits 表所有服務器上讀取數據。 遠程服務器不僅用於讀取數據,還會對盡可能數據做部分處理。 例如,對於使用 GROUP BY 的查詢,數據首先在遠程服務器聚合,之后返回聚合函數的中間狀態給查詢請求的服務器。再在請求的服務器上進一步匯總數據。
4.4. Memory
Memory 引擎以未壓縮的形式將數據存儲在RAM中。數據完全以讀取時獲得的形式存儲。換句話說,從這張表中讀取是很輕松的。並發數據訪問是同步的。鎖范圍小:讀寫操作不會相互阻塞。不支持索引。閱讀是並行化的。在簡單查詢上達到最大生產率(超過10 GB /秒),因為沒有磁盤讀取,不需要解壓縮或反序列化數據。(值得注意的是,在許多情況下,與 MergeTree 引擎的性能幾乎一樣高)。重新啟動服務器時,表中的數據消失,表將變為空。通常,使用此表引擎是不合理的。但是,它可用於測試,以及在相對較少的行(最多約100,000,000)上需要最高性能的查詢。
5. SQL語法
- SELECT查詢
SELECT [DISTINCT] expr_list [FROM [db.]table | (subquery) | table_function] [FINAL] [SAMPLE sample_coeff] [ARRAY JOIN ...] [GLOBAL] ANY|ALINNER|LEFT JOIN (subquery)|table USING columns_list [PREWHERE expr] [WHERE expr] [GROUP BY expr_list] [WITH TOTALS] [HAVING expr] [ORDER BY expr_list] [LIMIT [n, ]m] [UNION ALL ...] [INTO OUTFILE filename] [FORMAT format] [LIMIT n BY columns]
語法與關系型數據庫MYSQL基本一致。
除了SELECT之后的表達式列表(expr_list),所有的子句都是可選的。
- INSERT寫入
INSERT INTO [db.]table [(c1, c2, c3)] VALUES (v11, v12, v13), (v21, v22, v23), ...
語法與關系型數據庫MYSQL基本一致。
- remote數據同步
INSERT INTO [db.][tablename] SELECT * FROM remote(ip,db.tablename[,username[,password])
- mysql表同步
CREATE TABLE tmp ENGINE = MergeTree ORDER BY id AS SELECT * FROM mysql('host:port', 'database', 'table', 'user', 'password'[, replace_query, 'on_duplicate_clause']);
- CREATE創建
CREATE DATABASE [IF NOT EXISTS] db_name; CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = engine
- ALTER修改
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN ...
- DROP 刪庫/表
DROP DATABASE [IF EXISTS] db [ON CLUSTER cluster] #刪除數據庫 DROP [TEMPORARY] TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster] #刪除表
- ALTER修改
ALTER TABLE [db.]table DELETE WHERE filter_expr... #刪除數據 ALTER TABLE [db.]table UPDATE column1 = expr1 [, ...] WHERE ... #修改數據
- TRUNCATE 清表
TRUNCATE TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]
6. 實踐
ClickHouse在galaxy-0-10至galaxy-0-14五台物理機上有安裝部署,本次實踐是基於此測試集群進行操作。
- 分片配置文件路徑:/etc/metrika.xml
里面有配置分片名稱(cluster_3shards_1replicas)及信息,若要創建分布式庫/表,則在語句末尾加上ON CLUSTER cluster_3shards_1replicas即可。
登陸ClickHouse通過SQL也可查看分片信息:
- 配置文件路徑:/etc/clickhouse-server/config.xml
- 日志文件路徑:/var/log/clickhouse-server/
- 建表信息路徑:/data/clickhouse/metadata/
- 分區數據路徑:/data/clickhouse/data/
下面以進行分布式操作為例(單機操作省略[ON CLUSTER cluster_3shards_1replicas]即可):
6.1. 連接ClickHouse
clickhouse-client -m -u [username] -h [ip] --password [password] --port [port]
- username:用戶名
- password:密碼
- ip:服務器IP
- port:端口
- -m:允許多行查詢
6.2. 創建數據庫
創建分布式測試數據庫test
CREATE DATABASE test ON CLUSTER cluster_3shards_1replicas;
6.3. 創建表
以深圳通原始數據為例,在每個分片上創建表szt_data
CREATE TABLE test.szt_data ON CLUSTER cluster_3shards_1replicas ( id String, card_id String, deal_time String, trade_type String, trade_sum Int16, trade_value Int16, terminal_code String, com_line String, line_station String, car_gate String, flag String, finish_time Date ) ENGINE = MergeTree() PARTITION BY finish_time ORDER BY (card_id, terminal_code) SETTINGS index_granularity = 8192
以結算日期(Date型finish_time)按天進行分區,以卡號和設備編號進行排序,使用默認索引粒度8192。
- toYYYYMM(EventDate):按月分區
- toMonday(EventDate):按周分區
- toDate(EventDate):按天分區
- PARTITION BY cloumn_name:指定列分區(不建議以非日期類型作為分區)
對於分區可進行如下操作:
ALTER TABLE [db.][tablename] DROP PARTITION [partition] #刪除分區 ALTER TABLE [db.][tablename] DETACH PARTITION [partition]#下線分區 ALTER TABLE [db.][tablename] ATTACH PARTITION [partition]#恢復分區 ALTER TABLE [db.][tablename] FREEZE PARTITION [partition]#備份分區
創建分布式表all_szt_data
CREATE TABLE test.all_szt_data ON CLUSTER cluster_3shards_1replicas AS test.szt_data ENGINE = Distributed(cluster_3shards_1replicas, test, szt_data, rand())
- cluster_3shards_1replicas:配置文件中集群名稱
- test:數據庫名稱
- szt_data:表名
- rand():分片方式,隨機
6.4. 刪除庫/表
語法同mysql,若要刪除創建的分布式庫/表,在末尾加上ON CLUSTER cluster_3shards_1replicas即可。
DROP DATABASE test; #刪除數據庫 DROP TABLE szt_data; #刪除表 DROP TABLE all_szt_data; #刪除分布式表
6.5. 數據導入
上面建表時分別在各機器上創建了本地表(szt_data)和分布式表(all_szt_data),數據導入時可指定導入哪台機器上或者導入分布式表,若導入分布式表數據會隨機分到各分片上。
- 命令行導入
clickhouse-client -u [username] -h [ip] --password [password] --port [port] --query="INSERT INTO [tablename] FORMAT CSV" < [filepath] cat [filepath] | clickhouse-client -u [username] -h [ip] --password [password] --port [port] --query="INSERT INTO [tablename] FORMAT CSV"
同時可在clickhouse-client命令后面添加以下選項:
- --format_csv_delimiter:指定分隔符。上面命令中文件是以逗號分割
- --input_format_allow_errors_num:容錯數量
其他選項可輸入命令clickhouse-client --help查看。
6.6. 數據修改
語法與普通關系型數據庫稍微有些區別,由於分布式表並不存儲數據,所以無法直接對分布式表進行修改,對於本地表中數據指定記錄的修改,可用以下方式:
ALTER TABLE test.szt_data UPDATE trade_sum=1000 WHERE id='20181016094339_020001045'; #修改數據 ALTER TABLE test.szt_data DELETE WHERE id='20181016094339_020001045'; #刪除數據
6.7. 查詢
語法同普通關系型數據庫
SELECT * FROM test.szt_data LIMIT 10; #查詢本地表 SELECT * FROM test.all_szt_data LIMIT 10; #查詢分布式表
- 查看分片信息
SELECT * FROM system.clusters;
- 查看分區信息
SELECT partition, name, active FROM system.parts WHERE table = 'szt_data';
對應存儲目錄下的文件:
從目錄結構可看出,ClickHouse為列式存儲,每個數據文件夾下均存儲了以列為單位的文件。