ClickHouse
這裡目錄标題
- ClickHouse
-
- 1、軟體介紹
-
- 1.1. 簡介
- 1.2. 特點
- 1.3. 優點
- 1.4. 缺點
- 2、系統架構
-
- 2.1. Column與Field
- 2.2. 資料類型DataType
- 2.3. 塊Block
- 2.4. 塊流BlockStreams
- 2.5. Formats格式
- 2.6. 資料讀寫I/O
- 2.7. 資料表Table
- 2.8. 解析器Parser
- 2.9. 解釋器Interpreter
- 2.10. 函數Functions
-
- 2.10.1. 普通函數Functions
- 2.10.2. 聚合函數Aggregate Functions
- 2.11. Cluster與Replication
- 3、環境搭建
- 4、資料定義
-
- 4.1. 資料類型
-
- 4.1.1. 基本資料類型
- 4.1.2. 字元串
- 4.1.3. 日期時間
- 4.1.4. 複合類型
- 4.1.5. 其他類型
- 4.2. 資料庫操作
- 4.3. 資料表
-
- 4.3.1. 建立表
- 4.3.2. 删除表
- 4.3.3. 臨時表
- 4.3.4. 分區表
- 4.3.5. 資料表操作
- 4.4. 視圖
- 4.5. 資料的CRUD
-
- 4.5.1. 資料的寫入
- 4.5.2. 資料的删除和修改
- 5、MergeTree
-
- 5.1. 建立與存儲
-
- 5.1.2. 存儲格式
- 5.2. 資料分區
-
- 5.2.1. 資料分區規則
- 5.2.2. 分區目錄命名
- 5.2.3. 分區目錄合并
- 5.3. 一級索引
-
- 5.3.1. 稀疏索引
- 5.3.2. 索引粒度
- 5.3.3. 索引規則
- 5.3.4. 索引查詢過程
- 5.4. 二級索引
-
- 5.4.1. 粒度
- 5.4.2. 分類
- 5.5. 資料存儲
-
- 5.5.1. 列式存儲
- 5.5.2. 資料壓縮
- 5.6. 資料标記
-
- 5.6.1. 生成規則
- 5.6.2. 工作方式
- 5.7. 資料标記與資料壓縮
-
- 5.7.1. 多對一
- 5.7.2. 一對一
- 5.7.3. 一對多
- 5.8. 資料讀寫流程
-
- 5.8.1. 寫入資料
- 5.8.2. 查詢資料
- 5.9. 資料TTL
- 5.10. 多路徑存儲政策
- 6、 MergeTree Family
-
- 6.1. MergeTree
- 6.2. ReplacingMergeTree
- 6.3. SummingMergeTree
- 6.4. AggregatingMergeTree
- 6.5. CollapsingMergeTree
- 6.6. VersionedCollapsingMergeTree
- 7、常見類型表引擎
-
- 7.1. 外部存儲
-
- 7.1.1. HDFS
- 7.1.2. Mysql
- 7.1.3. JDBC
- 7.1.4. Kafka
- 7.1.5. File
- 7.2. 記憶體類型
-
- 7.2.1. Memory
- 7.2.2. Set
- 7.2.3. Join
- 7.3. 日志類型
-
- 7.3.1. TinyLog
- 7.3.2. StripeLog
- 7.3.3. Log
- 7.4. 接口類型
-
- 7.4.1. Merge
- 8、 資料查詢方式
-
- 8.1. With子句
- 8.2. From子句
- 8.3. Sample子句
- 8.4. Array Join子句
- 8.5. Join 子句
- 8.6. WHERE與PREWHERE子句
- 8.7. GROUP BY子句
-
- 8.7.1. WITH ROLLUP
- 8.7.2. WITH CUBE
- 8.7.3. WITH TOTALS
- 8.8. Having子句
- 8.9. ORDER BY子句
- 8.10. LIMIT BY子句
- 8.11. LIMIT子句
- 5、 副本與分片
1、軟體介紹
1.1. 簡介
ClickHouse 是開源的一個極具 " 戰鬥力 " 的實時資料分析資料庫,開發語言為C++,是一個用于聯機分析 (OLAP:Online Analytical Processing) 的列式資料庫管理系統(DBMS:Database Management System),簡稱 CK。
ClickHouse 的性能超過了目前市場上可比的面向列的DBMS。 每秒鐘每台伺服器每秒處理數 億至十億多行和數十千兆位元組的資料。
目前國内社群火熱,各個大廠紛紛跟進大規模使用:
- 今日頭條 内部用ClickHouse來做使用者行為分析;
- 騰訊内部用ClickHouse做遊戲資料分析,并且為之建立了一整套監控運維體系。
- 攜程内部從18年7月份開始接入試用,目前80%的業務都跑在ClickHouse上。
- 快手内部也在使用ClickHouse,存儲總量大約10PB, 每天新增200TB, 90%查詢小于3S
1.2. 特點
- 開源的列存儲資料庫管理系統,支援線性擴充,簡單友善,高可靠性,
- 容錯跑分快:比Vertica快5倍,比Hive快279倍,比MySQL快800倍,其可處理的資料級别已達到10 億級别
- https://clickhouse.tech/benchmark/dbms/
- 功能多:支援資料統計分析各種場景,支援類SQL查詢,異地複制部署
1.3. 優點
- 真正的面向列的DBMS(ClickHouse是一個DBMS,而不是一個單一的資料庫。它允許在運作時建立表和資料庫、加載資料和運作查詢,而無需重新配置和重新啟動伺服器)
- 資料壓縮(一些面向列的DBMS(INFINIDB CE 和 MonetDB)不使用資料壓縮。但是,資料壓縮确實是提高了性能)
- 磁盤存儲的資料(許多面向列的DBMS(SPA HANA和GooglePowerDrill))隻能在記憶體中工作。但即使在數千台伺服器上,記憶體也太小了。)
- 多核并行處理(多核多節點并行化大型查詢)
- 在多個伺服器上分布式處理(在clickhouse中,資料可以駐留在不同的分片上。每個分片都可以用于容錯的一組副本,查詢會在所有分片上并行處理)
- SQL支援(ClickHouse sql 跟真正的sql有不一樣的函數名稱。不過文法基本跟SQL文法相容,支援 JOIN/FROM/IN 和JOIN子句及标量子查詢支援子查詢)
- 向量化引擎(資料不僅按列式存儲,而且由矢量-列的部分進行處理,這使得開發者能夠實作高CPU 性能)
- 實時資料更新(ClickHouse支援主鍵表。為了快速執行對主鍵範圍的查詢,資料使用合并樹 (MergeTree)進行遞增排序。由于這個原因,資料可以不斷地添加到表中)
- 支援近似計算(統計全國到底有多少人?143456754 14.3E)
- 資料複制和對資料完整性的支援(ClickHouse使用異步多主複制。寫入任何可用的複本後,資料将分發到所有剩餘的副本。系統在不同的副本上保持相同的資料。資料在失敗後自動恢複)
1.4. 缺點
- 沒有完整的事務支援,不支援Transaction想快就别Transaction
- 缺少完整Update/Delete操作,缺少高頻率、低延遲的修改或删除已存在資料的能力,僅用于批量 删除或修改資料。
- 聚合結果必須小于一台機器的記憶體大小
- 支援有限作業系統,正在慢慢完善
- 不适合Key-value存儲,不支援Blob等文檔型資料庫
2、系統架構
- ClickHouse 是一個真正的列式資料庫管理系統(DBMS)。在 ClickHouse 中,資料始終是按列存儲的,包括矢量(向量或列塊)執行的過程。
2.1. Column與Field
-
和Column
是Field
資料最基礎的映射單元。ClickHouse
- 記憶體中的一列資料由一個Column對象表示。
- Field對象代表一個單值;操作單個具體的數值 ( 也就是單列中的一行資料 )。
- Column對象分為接口和實作兩個部分,在IColumn接口對象中,定義了對資料進行各種關系運算的方法。
- 幾乎所有的操作都是不可變的:這些操作不會更改原始列,但是會建立一個新的修改後的 列。
- Field對象使用了聚合的設計模式。在Field對象内部聚合了 Null、UInt64、String和Array等13種資料類型及相應的處理邏輯。
2.2. 資料類型DataType
- DataType 描述了列的資料類型;
- DataType 僅存儲中繼資料。
- 負責序列化和反序列化:讀寫二進制或文本形式的列或單個值構成的塊。
- DataType雖然負責序列化相關工作,但它并不直接負責資料的讀取,而是轉由從Column或Field對 象擷取。
2.3. 塊Block
- Block 是表示記憶體中表的子集(chunk)的容器,由三元組 (cloumn,datatype,列名)構成的集合;
- 表操作的對象是Block;Block對象可以看作資料表的子集。
- Block并沒有直接聚合Column和DataType對象,而是通過ColumnWithTypeAndName對象進行間接引用。
2.4. 塊流BlockStreams
- 塊流用于處理資料,負責塊資料的讀取和寫出。
- Block流操作有兩組頂層接口:
- IBlockInputStream負責資料的讀取和關系運算,IBlockInputStream 具有read方法,其能夠在資料可用時擷取下一個塊。
- IBlockOutputStream負責将資料輸出到下一環節。IBlockOutputStream 具有 write 方法,其能夠将塊寫到某處。
2.5. Formats格式
- 資料格式同塊流一起實作。用于向用戶端展示資料的方式;
- 如塊流 IBlockOutputStream 提供的 Pretty 格式,也有其它輸入輸出格式,比如 TabSeparated 或 JSONEachRow 。
- 如行流: IRowInputStream 和 IRowOutputStream 。它們允許你按行 pull/push 資料,而不是按 塊。
2.6. 資料讀寫I/O
- 有一個緩沖區負責資料的讀寫;
- 面向位元組的輸入輸出,有 ReadBuffer 和 WriteBuffer 這兩個抽象類。
2.7. 資料表Table
- 多個列的一個集合體。
- 讀取資料的時候以表為機關進行操作,操作的時候以BlockStream操作Block;
- 在資料表的底層設計中并沒有所謂的Table對象;
- 表由 IStorage 接口表示。該接口的不同實作對應不同的表引擎。
2.8. 解析器Parser
- 查詢由一個手寫遞歸下降解析器解析。
- 對Sql語句進行解析為AST文法樹。
2.9. 解釋器Interpreter
- 解釋AST文法樹。
- 解釋器負責從 AST 建立查詢執行流水線。
- 簡單的解釋器,如 InterpreterExistsQuery 和 InterpreterDropQuery
- 複雜的解釋器,如 InterpreterSelectQuery 。
- 查詢執行流水線由塊輸入或輸出流組成。
2.10. 函數Functions
- 單行函數(Functions)
- 組函數(Aggregate Functions)
2.10.1. 普通函數Functions
- 普通函數不會改變行數 - 它們的執行看起來就像是獨立地處理每一行資料。
- 無狀态的普通函數,
- 是作用在以 Block 為機關的資料上,以實作向量查詢執行。
2.10.2. 聚合函數Aggregate Functions
- 聚合函數是狀态函數。它們将傳入的值激活到某個狀态,并允許你從該狀态擷取結果。
- 聚合函數是有狀态的
- 聚合狀态可以被序列化和反序列化,以在分布式查詢執行期間通過網絡傳遞或者在記憶體不夠的時候 将其寫到硬碟。
2.11. Cluster與Replication
- ClickHouse的叢集由分片 ( Shard ) 組成,而每個分片又通過副本 ( Replica ) 組成。
- 這種分層的概念,在一些流行的分布式系統中十分普遍。
- ClickHouse的1個節點隻能擁有1個分片,也就是說如果要實作1分片、1副本,則至少需要部 署2個服務節點。
- 分片隻是一個邏輯概念,其實體承載還是由副本承擔的。
3、環境搭建
- ClickHouse 21.6.8.62 環境搭建
- https://blog.csdn.net/weixin_43660536/article/details/120090681.
4、資料定義
4.1. 資料類型
4.1.1. 基本資料類型
- 整數Int8->1位元組、Int16->2位元組、Int32->4位元組 和 Int64->8位元組
- 浮點數 Float32 和 Float64
- 定點數 Decimal32、Decimal64 和Decimal128
- 布爾 UInt8 限制值為0或1
4.1.2. 字元串
- String、FixedString 和 UUID
- String 不限制長度,相當于Varchar、Text、Clob 和 Blob 等字元類型
- FixedString(N)相當于Char,長度固定,資料長度不夠時,添加空位元組(null);長度過長傳回錯誤消息
- UUID:32位,格式8-4-4-4-12,如果未被指派,則用0填充
4.1.3. 日期時間
- Date、DateTime、DateTime64
- Date: 2020-02-02 精确到天
- DateTime: 2020-02-02 20:20:20 精确到秒
- DateTime64: 2020-02-02 20:20:20.335 精确到亞秒,可以設定精度
4.1.4. 複合類型
- 數組
- 建立資料:array(T)或[],類型必須相同
- 元組
- 由多個元素組成,允許不同類型
- 建立資料:(T1, T2, …),Tuple(T1, T2, …)
- 枚舉類型
- ClickHouse提供了Enum8和Enum16兩種枚舉類型,它們除了取值範圍不同之外,别無二 緻。
- 枚舉固定使用(String:Int)Key/Value鍵值對的形式定義資料,是以Enum8和Enum16分别會對 應(String:Int8)和(String:Int16)
- 用(String:Int) Key/Value鍵值對的形式定義資料,鍵值對不能同時為空,不允許重複,key允許 為空字元串,需要看到對應的值進行轉換
- 嵌套類型
- Nested(Name1 Type1,Name2 Type2,…)
- 相當于表中嵌套一張表,插入時相當于一個多元數組的格式,一個字段對應一個數組。
4.1.5. 其他類型
- Nullable(TypeName)
- 隻能與基礎資料類型搭配使用,表示某個類型的值可以為NULL;Nullable(Int8)表示可以存儲 Int8類型的值,沒有值時存NULL
- 注意:
- 不能與複合類型資料一起使用
- 不能作為索引字段
- 盡量避免使用,字段被Nullable修飾後會額外生成[Column].null.bin 檔案儲存Null值, 增加開銷
- Domain
- Pv4 使用 UInt32 存儲。如 116.253.40.133
- IPv6 使用 FixedString(16) 存儲。如 2a02:aa08:e000:3100::2
4.2. 資料庫操作
- 資料庫起到了命名空間的作用,可以有效規避命名沖突的問題,也為後續的資料隔離提供了支撐。 任何一張資料表,都必須歸屬在某個資料庫之下。
CREATE DATABASE IF NOT EXISTS db_name [ENGINE = engine]
SHOW DATABASES
DROP DATABASE [IF EXISTS] db_name
- 資料庫引擎
- Ordinary:預設引擎
- 在絕大多數情況下我們都會使用預設引擎,使用時無須刻意聲明。在此資料庫下可以使 用任意類型的表引擎。
- Dictionary:字典引擎
- 此類資料庫會自動為所有資料字典建立它們的資料表,關于資料字典的詳細介紹會在第5 章展開。
- Memory:記憶體引擎
- 用于存放臨時資料。此類資料庫下的資料表隻會停留在記憶體中,不會涉及任何磁盤操 作,當服務重新開機後資料會被清除。
- Lazy:日志引擎
- 此類資料庫下隻能使用Log系列的表引擎,關于Log表引擎的詳細介紹會在第8章展開。
- MySQL:MySQL引擎
- 此類資料庫下會自動拉取遠端MySQL中的資料,并為它們建立MySQL表引擎的資料表,
- Ordinary:預設引擎
4.3. 資料表
4.3.1. 建立表
- ClickHouse目前提供了三種最基本的建表方法
- 第一種是正常定義方法
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
省略…
) ENGINE = engine
CREATE TABLE hits_v1 (
Title String,
URL String ,
EventTime DateTime
) ENGINE = Memory;
- 使用[db_name.]參數可以為資料表指定資料庫,如果不指定此參數,則預設會使用default數 據庫。
第二種定義方法是複制其他表的結構
CREATE TABLE [IF NOT EXISTS] [db_name1.]table_name AS [db_name2.]
table_name2 [ENGINE = engine]
--建立新的資料庫
CREATE DATABASE IF NOT EXISTS new_db
--将default.hits_v1的結構複制到new_db.hits_v1
CREATE TABLE IF NOT EXISTS new_db.hits_v1 AS default.hits_v1 ENGINE =
TinyLog
- 支援在不同的資料庫之間複制表結構
第三種定義方法是通過SELECT子句的形式建立
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name ENGINE = engine AS
SELECT …
CREATE TABLE IF NOT EXISTS hits_v1_1 ENGINE = Memory AS SELECT * FROM
hits_v1
- 根據SELECT子句建立相應的表結構,同時還會将SELECT子句查詢的資料順帶寫入
4.3.2. 删除表
- ClickHouse和大多數資料庫一樣,使用DESC查詢可以傳回資料表的定義結構。
- 如果想删除一張資料表,則可以使用下面的DROP語句:
DROP TABLE [IF EXISTS] [db_name.]table_name
4.3.3. 臨時表
- ClickHouse也有臨時表的概念,建立臨時表的方法是在普通表的基礎之上添加
關鍵字。TEMPORARY
- 特點
- 生命周期是會話綁定的,是以它隻支援Memory表引擎,如果會話結束,資料表就會被銷毀;
- 臨時表不屬于任何資料庫,是以在它的建表語句中,既沒有資料庫參數也沒有表引擎參數。
- 臨時表的優先級是大于普通表的。當兩張資料表名稱相同的時候,會優先讀取臨時表的資料
4.3.4. 分區表
- 資料分區(partition)和資料分片(shard)是完全不同的兩個概念。
- 資料分區是資料的一種縱向切分。而資料分片是資料的一種橫向切分;
4.3.5. 資料表操作
- 追加新字段
-
ALTER TABLE tb_name ADD COLUMN [IF NOT EXISTS] name [type] [default_expr] [AFTER name_after]ALTER TABLE testcol_v1 ADD COLUMN OS String DEFAULT 'mac'ALTER TABLE testcol_v1 ADD COLUMN IP String AFTER ID
-
- 修改字段類型
-
ALTER TABLE tb_name MODIFY COLUMN [IF EXISTS] name [type] [default_expr] ALTER TABLE testcol_v1 MODIFY COLUMN IP IPv4
-
- 修改備注
-
ALTER TABLE tb_name COMMENT COLUMN [IF EXISTS] name 'some comment' ALTER TABLE testcol_v1 COMMENT COLUMN ID '主鍵ID' DESC testcol_v1
-
- 删除已有字段
-
ALTER TABLE tb_name DROP COLUMN [IF EXISTS] name ALTER TABLE testcol_v1 DROP COLUMN URL
-
- 清空資料表
-
TRUNCATE TABLE [IF EXISTS] [db_name.]tb_name TRUNCATE TABLE db_test.testcol_v2
-
4.4. 視圖
- ClickHouse擁有普通和物化兩種視圖,其中物化視圖擁有獨立的存儲,而普通視圖隻是一層簡單的查詢代理。
- 普通視圖
- 普通視圖不會存儲任何資料,它隻是一層單純的SELECT查詢映射,起着簡化查詢、明晰語義 的作用,對查詢性能不會有任何增強。
- 物化視圖
-
CREATE [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]table_name [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT .
- 物化視圖支援表引擎,資料儲存形式由它的表引擎決定 ;
- 物化視圖建立好之後,如果源表被寫入新資料,那麼物化視圖也會同步更新。
- POPULATE修飾符決定了物化視圖的初始化政策:
- 如果使用了POPULATE修飾符,那麼在建立視圖的過程中,會連帶将源表中已存在的資料一并導入,如同執行了SELECT INTO一般;
- 反之,如果不使用POPULATE修飾符,那麼物化視圖在建立之後是沒有資料的,它隻會 同步在此之後被寫入源表的資料。
- 物化視圖目前并不支援同步删除,如果在源表中删除了資料,物化視圖的資料仍會保 留。
-
4.5. 資料的CRUD
4.5.1. 資料的寫入
- INSERT語句支援三種文法範式,三種範式各有不同,可以根據寫入的需求靈活運用。
- 第一種是使用VALUES格式的正常文法:
- 第二種是使用指定格式的文法:
- 第三種是使用SELECT子句形式的文法
4.5.2. 資料的删除和修改
- ClickHouse提供了DELETE和UPDATE的能力,這類操作被稱為Mutation查詢,它可以看作ALTER 語句的變種。
- 雖然Mutation能最終實作修改和删除,但不能完全以通常意義上的UPDATE和DELETE來了解,我 們必須清醒地認識到它的不同:
- 首先,Mutation語句是一種“很重”的操作,更适用于批量資料的修改和删除;
- 其次,它不支援事務,一旦語句被送出執行,就會立刻對現有資料産生影響,無法復原;
- 最後,Mutation語句的執行是一個異步的背景過程,語句被送出之後就會立即傳回。
- DELETE語句的完整文法
- UPDATE語句的完整文法
5、MergeTree
- 表引擎是ClickHouse設計實作中的一大特色
- ClickHouse擁有非常龐大的表引擎體系,其共擁有
、合并樹
、外部存儲
、記憶體
、檔案
和其他6大類20多種表引擎。接口
- 合并樹家族自身也擁有多種表引擎的變種。其中MergeTree作為家族中最基礎的表引擎;
- 提供了
、主鍵索引
、資料分區
和資料副本
等基本能力;資料采樣
5.1. 建立與存儲
- MergeTree在寫入一批資料時,資料總會以資料片段的形式寫入磁盤,且資料片段不可修改。
- 為了避免片段過多,ClickHouse會通過背景線程,定期合并這些資料片段,屬于相同分區的資料片段會被合成一個新的片段。
- 這種資料片段往複合并的特點,也正是合并樹名稱的由來。
- 配置選項
- PARTITION BY [選填]:分區鍵,用于指定表資料以何種标準進行分區。
- ORDER BY [必填]:排序鍵,用于指定在一個資料片段内,資料以何種标準排序。
- 預設情況下主鍵(PRIMARY KEY)與排序鍵相同。
- PRIMARY KEY [選填]:主鍵,顧名思義,聲明後會依照主鍵字段生成一級索引,用于加速表 查詢。
- SAMPLE BY [選填]:抽樣表達式,用于聲明資料以何種标準進行采樣。
- SETTINGS:
- index_granularity [選填]:索引粒度 預設8192
- index_granularity_bytes [選填]:自适應間隔大小(粒度),預設10M;
- enable_mixed_granularity_parts [選填] 是否開啟自适應間隔,預設開啟;
- merge_with_ttl_timeout [選填] TTL的功能;
- storage_policy [選填]:提供了多路徑的存儲政策;
5.1.2. 存儲格式
- MergeTree表引擎中的資料是擁有實體存儲的,資料會按照分區目錄的形式儲存到磁盤之上。
- 一張資料表的完整實體結構分為3個層級,依次是資料表目錄、分區目錄及各分區下具體的資料檔案;
- partition:分區目錄,餘下各類資料檔案(primary.idx、[Column].mrk、[Column].bin等)
- 以分區目錄的形式被組織存放的,屬于相同分區的資料,最終會被合并到同一個分區目錄。
- checksums.txt:校驗檔案,使用二進制格式存儲。它儲存了餘下各類檔案(primary.idx、 count.txt等)的size大小及size的哈希值,用于快速校驗檔案的完整性和正确性。
- columns.txt:列資訊檔案,使用明文格式存儲。用于儲存此資料分區下的列字段資訊
- count.txt:計數檔案,使用明文格式存儲。用于記錄目前資料分區目錄下資料的總行數,
- primary.idx:一級索引檔案,使用二進制格式存儲。用于存放稀疏索引,一張MergeTree表 隻能聲明一次一級索引(通過ORDERBY或者PRIMARY KEY)。
- [Column].bin:資料檔案,使用壓縮格式存儲,預設為LZ4壓縮格式,用于存儲某一列的資料。
- [Column].mrk:列字段标記檔案,使用二進制格式存儲。标記檔案中儲存了.bin檔案中資料 的偏移量資訊。
- [Column].mrk2:如果使用了自适應大小的索引間隔,則标記檔案會以.mrk2命名。它的工作 原理和作用與.mrk标記檔案相同。
- partition.dat與minmax_[Column].idx:如果使用了分區鍵,例如PARTITION BY EventTime,則會額外生成partition.dat與minmax索引檔案,它們均使用二進制格式存儲。
- skp_idx[Column].idx與skp_idx[Column].mrk:如果在建表語句中聲明了二級索引,則會額外 生成相應的二級索引與标記檔案,它們同樣也使用二進制存儲。二級索引在ClickHouse中又 稱跳數索引,
5.2. 資料分區
5.2.1. 資料分區規則
- MergeTree資料分區的規則由分區ID決定,而具體到每個資料分區所對應的ID,則是由分區鍵的取值決定的。
- 針對取值資料類型的不同,分區ID的生成邏輯目前擁有四種規則:
5.2.2. 分區目錄命名
- 一個完整分區目錄的命名公式
-
- 201905表示分區目錄的ID;
- 1_1分别表示最小的資料塊編号與最大的資料塊編号;
- 而最後的_0則表示目前合并的層級。
- PartitionID_MinBlockNum_MaxBlockNum_Level
5.2.3. 分區目錄合并
- MergeTree的分區目錄并不是在資料表被建立之後就存在的,而是在資料寫入過程中被建立的。
- 也就是說如果一張資料表沒有任何資料,那麼也不會有任何分區目錄存在。
- MergeTree伴随着每一批資料的寫入(一次INSERT語句),MergeTree都會生成一批新的分區目錄。
- 即便不同批次寫入的資料屬于相同分區,也會生成不同的分區目錄。
- 寫入後的10~15分鐘,也可以手動執行optimize查詢語句)
- ClickHouse會通過背景任務再将屬于相同分區的多個目錄合并成一個新的目錄。
- 已經存在的舊分區目錄并不會立即被删除,而是在之後的某個時刻通過背景任務被删除(預設 8分鐘)。
- 新目錄名稱的合并方式遵循規則:
- MinBlockNum:取同一分區内所有目錄中最小的MinBlockNum值。
- MaxBlockNum:取同一分區内所有目錄中最大的MaxBlockNum值。
- Level:取同一分區内最大Level值并加1。
5.3. 一級索引
- MergeTree的主鍵使用PRIMARY KEY定義,
- 待主鍵定義之後,MergeTree會依據 index_granularity間隔(預設8192行),為資料表生成一級索引并儲存至primary.idx檔案内,索引資料按照PRIMARYKEY排序。
5.3.1. 稀疏索引
- primary.idx檔案内的一級索引采用稀疏索引實作。
- 稠密索引中每一行索引标記都會對應到一行具體的資料記錄。
- 稀疏索引中每一行索引标記對應的是一段資料,而不是一行。
- 由于稀疏索引占用空間小,是以primary.idx内的索引資料常駐記憶體,取用速度自然極快。
5.3.2. 索引粒度
- 索引粒度就如同标尺一般,會丈量整個資料的長度,并依照刻度對資料進行标注,最終将資料标記成多個間隔的小段。
5.3.3. 索引規則
- 由于是稀疏索引,是以MergeTree需要間隔index_granularity行資料才會生成一條索引記錄,其索 引值會依據聲明的主鍵字段擷取。
- 單主鍵
- 第0(81920)行CounterID取值57,第8192(81921)行CounterID取值1635,而第 16384(8192*2)行CounterID取值3266
- 最終索引資料将會是5716353266。
- 多主鍵
- 聯合主鍵或多個字段列充當主鍵,多個ID放在一起。
5.3.4. 索引查詢過程
- MarkRange
- MarkRange在ClickHouse中是用于定義标記區間的對象。
- 按照index_granularity的間隔粒度,将一段完整的資料劃分成了多個小的間隔資料段,一個具體的資料段即是一個MarkRange。
- MarkRange與索引編号對應,使用start和end兩個屬性表示其區間範圍。
- 通過與start及end對應的索引編号的取值,即能夠得到它所對應的數值區間。而數值區間表示 了此MarkRange包含的資料範圍。
- 索引查詢其實就是兩個數值區間的交集判斷。
- 一個區間是由基于主鍵的查詢條件轉換而來的條件區間;
- 一個區間是剛才所講述的與MarkRange對應的數值區間。
- 查詢步驟
- 生成查詢條件區間:首先,将查詢條件轉換為條件區間。
WHERE ID = 'A003' ['A003', 'A003']
- 遞歸交集判斷:以遞歸的形式,依次對MarkRange的數值區間與條件區間做交集判斷。從最 大的區間[A000,+inf)開始:
- 如果不存在交集,則直接通過剪枝算法優化此整段MarkRange。
- 如果存在交集,且MarkRange步長大于8(end-start),則将此區間進一步拆分成8個子區 間,并重複此規則,繼續做遞歸交集判斷。
- 如果存在交集,且MarkRange不可再分解(步長小于8),則記錄MarkRange并傳回。
- 合并MarkRange區間:将最終比對的MarkRange聚在一起,合并它們的範圍。
5.4. 二級索引
- 二級索引又稱跳數索引,由資料的聚合資訊建構而成。
- 根據索引類型的不同,其聚合資訊的内容也不同。跳數索引的目的與一級索引一樣,也是幫助查詢時減少資料掃描的範圍。
- 跳數索引在預設情況下是關閉的,需要設定allow_experimental_data_skipping_indices
- SET allow_experimental_data_skipping_indices = 1
- 跳數索引需要在CREATE語句内定義,它支援使用元組和表達式的形式聲明,其完整的定義文法
- INDEX index_name expr TYPE index_type(…) GRANULARITY granularity
5.4.1. 粒度
- 定義了一行跳數索引能夠跳過多少個index_granularity區間的資料(預設8192行)。
- 按照index_granularity粒度間隔将資料劃分成n段,總共有[0,n-1]個區(n=total_rows/index_granularity,向上取整)
- 當移動index_granularity次區間時,則彙總并生成一行跳數索引資料。
5.4.2. 分類
- MergeTree共支援4種跳數索引,分别是minmax、set、ngrambf_v1和tokenbf_v1。一張資料表支援同時聲明多個跳數索引。
-
CREATE TABLE skip_test ( ID String, URL String, Code String, EventTime Date, INDEX a ID TYPE minmax GRANULARITY 5, INDEX b(length(ID) * 8) TYPE set(2) GRANULARITY 5, INDEX c(ID,Code) TYPE ngrambf_v1(3, 256, 2, 0) GRANULARITY 5, INDEX d ID TYPE tokenbf_v1(256, 2, 0) GRANULARITY 5 ) ENGINE = MergeTree()
- minmax
- minmax索引記錄了一段資料内的最小和最大極值,其索引的作用類似分區目錄的minmax索 引,能夠快速跳過無用的資料區間.
- set
- set索引直接記錄了聲明字段或表達式的取值(唯一值,無重複),其完整形式為 set(max_rows),其中max_rows是一個阈 值,表示在一個index_granularity内,索引最多記錄的資料行數。
- ngrambf_v1:
- ngrambf_v1索引記錄的是資料短語的布隆表過濾器,隻支援String和FixedString資料類型。
- ngrambf_v1隻能夠提升in、notIn、like、equals和notEquals查詢的性能.
- tokenbf_v1:
- tokenbf_v1索引是ngrambf_v1的變種,同樣也是一種布隆過濾器索引。
- tokenbf_v1除了短語token的處理方法外,其他與ngrambf_v1是完全一樣的。
- tokenbf_v1會自動按照非字元的、數字的字元串分割token。
5.5. 資料存儲
5.5.1. 列式存儲
- 在MergeTree中,資料按列存儲。而具體到每個列字段,資料也是獨立存儲的,每個列字段都擁有 一個與之對應的.bin資料檔案。也正是這些.bin檔案,最終承載着資料的實體存儲。
- 資料檔案以分區目錄的形式被組織存放,是以在.bin檔案中隻會儲存目前分區片段内的這一部分資料
- 優勢:
- 一是可以更好地進行資料壓縮
- 二是能夠最小化資料掃描的範圍
- 存儲方式
- 首先,資料是經過壓縮的,目前支援LZ4、ZSTD、Multiple和Delta幾種算法,預設使用LZ4算 法;
- 其次,資料會事先依照ORDER BY的聲明排序;
- 最後,資料是以壓縮資料塊的形式被組織并寫入.bin檔案中的。
5.5.2. 資料壓縮
- 一個壓縮資料塊由頭資訊和壓縮資料兩部分組成。
- 頭資訊固定使用9位位元組表示,具體由1個UInt8(1位元組)整型和2個UInt32(4位元組)整型組成 。
- 分别代表使用的壓縮算法類型、壓縮後的資料大小和壓縮前的資料大小 。
- bin壓縮檔案是由多個壓縮資料塊組成的,而每個壓縮資料塊的頭資訊則是基于CompressionMethod_CompressedSize_UncompressedSize公式生成的。
- 每個壓縮資料塊的體積,按照其壓縮前的資料位元組大小,都被嚴格控制在64KB~1MB。
- 其上下限分别由min_compress_block_size(預設65536)與max_compress_block_size(默 認1048576)參數指定。
- 一個壓縮資料塊最終的大小,則和一個間隔(index_granularity)内資料的實際大小相關。
- 資料寫入過程
- MergeTree在資料具體的寫入過程中,會依照索引粒度(預設情況下,每次取8192行),按批次擷取資料并進行處理
- 單個批次資料size<64KB :如果單個批次資料小于64KB,則繼續擷取下一批資料,直至累積到size>=64KB時,生成下一個壓縮資料塊。
- 單個批次資料64KB<=size<=1MB :如果單個批次資料大小恰好在64KB與1MB之間,則直接生成下一個壓縮資料塊。
- 單個批次資料size>1MB :如果單個批次資料直接超過1MB,則首先按照1MB大小截斷并生成 下一個壓縮資料塊。剩餘資料繼續依照上述規則執行。
- 優勢:
- 其一,雖然資料被壓縮後能夠有效減少資料大小,降低存儲空間并加速資料傳輸效率,但資料的壓縮和解壓動作,其本身也會帶來額外的性能損耗。是以需要控制被壓縮資料的大小,以求 在性能損耗和壓縮率之間尋求一種平衡。
- 其二,在具體讀取某一列資料時(.bin檔案),首先需要将壓縮資料加載到記憶體并解壓,這樣才能進行後續的資料處理。通過壓縮資料塊,可以在不讀取整個.bin檔案的情況下将讀取粒度降低到壓縮資料塊級别,進而進一步縮小資料讀取的範圍。
5.6. 資料标記
5.6.1. 生成規則
- 資料标記作為銜接一級索引和資料的橋梁,其像極了做過标記小抄的書簽,而且書本中每個一級章節都擁有各自的書簽。
- 資料标記和索引區間是對齊的,均按照index_granularity的粒度間隔。
- 為了能夠與資料銜接,資料标記檔案也與.bin檔案一一對應。
- 每一個列字段[Column].bin檔案都有一個與之對應的[Column].mrk資料标記檔案,用于記錄 資料在.bin檔案中的偏移量資訊。
- 一行标記資料使用一個元組表示,元組内包含兩個整型數值的偏移量資訊。
- 它們分别表示在此段資料區間内,在對應的.bin壓縮檔案中,壓縮資料塊的起始偏移量;
- 以及将該資料壓縮塊解壓後,其未壓縮資料的起始偏移量。
- 每一行标記資料都表示了一個片段的資料(預設8192行)在.bin壓縮檔案中的讀取位置資訊。标記 資料與一級索引資料不同,它并不能常駐記憶體,而是使用LRU(最近最少使用)緩存政策加快其取用速度。
- 标記檢視指令
-
od -An -l name.mrk
-
5.6.2. 工作方式
- MergeTree在讀取資料時,必須通過标記資料的位置資訊才能夠找到所需要的資料。整個查找過程大緻可以分為讀取壓縮資料塊和讀取資料兩個步驟
- 資料了解
- 1B*8192=8192B,64KB=65536B,65536/8192=8
- 頭資訊固定由9個位元組組成,壓縮後大小為8個位元組
- 12016=8+12000+8
- 讀取壓縮資料塊:
- 在查詢某一列資料時,MergeTree無須一次性加載整個.bin檔案,而是可以根據需要,隻加載特定的壓縮資料塊。而這項特性需要借助标記檔案中所儲存的壓縮檔案中的偏移量。
- 讀取資料:
- 在讀取解壓後的資料時,MergeTree并不需要一次性掃描整段解壓資料,它可以根據需要, 以index_granularity的粒度加載特定的一小段。為了實作這項特性,需要借助标記檔案中儲存的解壓資料塊中的偏移量。
5.7. 資料标記與資料壓縮
- 由于壓縮資料塊的劃分,與一個間隔(index_granularity)内的資料大小相關,每個未壓縮資料塊的體積都被嚴格控制在64KB~1MB。
- 而一個間隔(index_granularity)的資料,又隻會産生一行資料标記。
- 那麼根據一個間隔内資料的實際位元組大小,資料标記和壓縮資料塊之間會産生三種不同的對應關系。
5.7.1. 多對一
- 多個資料标記對應一個壓縮資料塊 當一個間隔(index_granularity)内的資料未壓縮大小size小于64KB時
5.7.2. 一對一
- 一個資料标記對應一個壓縮資料塊 當一個間隔(index_granularity)内的資料未壓縮大小size大于等于64KB且小于等于1MB時
5.7.3. 一對多
- 一個資料标記對應多個壓縮資料塊 當一個間隔(index_granularity)内的資料未壓縮大小size直接大于1MB時
5.8. 資料讀寫流程
5.8.1. 寫入資料
- 資料寫入的第一步是生成分區目錄,伴随着每一批資料的寫入,都會生成一個新的分區目錄。在後 續的某一時刻,屬于相同分區的目錄會依照規則合并到一起;接着,按照index_granularity索引粒 度,會分别生成primary.idx一級索引、每一個列字段的.mrk資料标記和.bin壓縮資料檔案。
- 從分區目錄201403_1_34_3能夠得知,該分區資料共分34批寫入,期間發生過3次合并。在數 據寫入的過程中,依據index_granularity的粒度,依次為每個區間的資料生成索引、标記和 壓縮資料塊。其中,索引和标記區間是對齊的,而标記與壓縮塊則根據區間資料大小的不同, 會生成多對一、一對一和一對多三種關系。
5.8.2. 查詢資料
- 資料查詢的本質,可以看作一個不斷減小資料範圍的過程。在最理想的情況下,MergeTree首先可 以依次借助分區索引、一級索引和二級索引,将資料掃描範圍縮至最小。然後再借助資料标記,将 需要解壓與計算的資料範圍縮至最小。
5.9. 資料TTL
- TTL即Time To Live,顧名思義,它表示資料的存活時間。在MergeTree中,可以為某個列字段或整張表設定TTL。
- 當時間到達時,如果是列字段級别的TTL,則會删除這一列的資料;
- 如果是表級别的TTL,則會删除整張表的資料;
- 如果同時設定了列級别和表級别的TTL,則會以先到期的那個為主。
- 設定TTL
- INTERVAL完整的操作包括SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 和YEAR。
- ClickHouse沒有提供取消TTL的方法。
- 列級别設定TTL
-- create_time是日期類型,列字段code與type均被設定了TTL,它們的存活時間是在 -- create_time的取值基礎之上向後延續10秒 CREATE TABLE ttl_table_v1( id String, create_time DateTime, code String TTL create_time + INTERVAL 10 SECOND, type UInt8 TTL create_time + INTERVAL 10 SECOND ) ENGINE = MergeTree PARTITION BY toYYYYMM(create_time) ORDER BY id; INSERT INTO TABLE ttl_table_v1 VALUES('A000',now(),'C0',1), ('A001',now() + INTERVAL 10 MINUTE,'C1',1); SELECT * FROM ttl_table_v1 -- 執行optimize指令強制觸發TTL清理 -- 第一行資料滿足TTL過期條件(目前系統時間>=create_time+10秒),它們的code和 -- type列會被還原為資料類型的預設值: optimize TABLE ttl_table_v1 FINAL -- 修改列字段的TTL,或是為已有字段添加TTL,則可以使用ALTER語句 ALTER TABLE ttl_table_v1 MODIFY COLUMN code String TTL create_time + INTERVAL 1 DAY
- 表級别設定TTL
- TTL的運作機制
- 如果一張MergeTree表被設定了TTL表達式,那麼在寫入資料時,會以資料分區為機關,在每 個分區目錄内生成一個名為ttl.txt的檔案。
- ttl.txt檔案中通過一串JSON配置儲存了TTL的相關資訊
- {“columns”:[{“name”:“code”,“min”:1557478860,“max”:1557651660}],“table”: {“min”:1557565200,“max”:1557738000}}
- columns用于儲存列級别TTL資訊;
- table用于儲存表級别TTL資訊;
- min和max則儲存了目前資料分區内,TTL指定日期字段的最小值、最大值分别與 INTERVAL表達式計算後的時間戳。
5.10. 多路徑存儲政策
- 19.15版本之前,MergeTree隻支援單路徑存儲,所有的資料都會被寫入config.xml配置中path指定的路徑下,即使伺服器挂載了多塊磁盤,也無法有效利用這些存儲空間。
- 19.15版本開始,MergeTree實作了自定義存儲政策的功能,支援以資料分區為最小移動單元,将分區目錄寫入多塊磁盤目錄。
- 存儲政策
- 預設政策
- MergeTree原本的存儲政策,無須任何配置,所有分區會自動儲存到config.xml配置中 path指定的路徑下.
- JBOD政策
- 這種政策适合伺服器挂載了多塊磁盤,但沒有做RAID的場景。
- JBOD的全稱是Just a Bunch of Disks,它是一種輪詢政策,每執行一次INSERT或者 MERGE,所産生的新分區會輪詢寫入各個磁盤。
- HOT/COLD政策
- 這種政策适合伺服器挂載了不同類型磁盤的場景。
- 将存儲磁盤分為HOT與COLD兩類區域。
- HOT區域使用SSD這類高性能存儲媒介,注重存取性能;
- COLD區域則使用HDD這類高容量存儲媒介,注重存取經濟性。
- 資料在寫入MergeTree之初,首先會在HOT區域建立分區目錄用于儲存資料,當分區資料大小累積到門檻值時,資料會自行移動到COLD區域。
- 預設政策
- 配置方式
- 存儲配置需要預先定義在config.xml配置檔案中,由storage_configuration标簽表示。
- 在storage_configuration之下又分為disks和policies兩組标簽,分别表示磁盤與存儲政策。
<storage_configuration>
<disks>
<disk_name_a> <!--自定義磁盤名稱 -->
<path>/chbase/data</path><!—磁盤路徑 -->
<keep_free_space_bytes>1073741824</keep_free_space_bytes>
</disk_name_a>
<disk_name_b>
<path>… </path>
<keep_free_space_bytes>...</keep_free_space_bytes>
</disk_name_b>
</disks>
<policies>
<default_jbod> <!--自定義政策名稱 -->
<volumes>
<jbod> <!—自定義名稱 磁盤組 -->
<disk>disk_name_a</disk>
<disk>disk_name_b</disk>
</jbod>
</volumes>
</default_jbod>
</policies>
</storage_configuration>
6、 MergeTree Family
6.1. MergeTree
- MergeTree表引擎向下派生出6個變種表引擎。
6.2. ReplacingMergeTree
- MergeTree擁有主鍵,但是它的主鍵卻沒有唯一鍵的限制。這意味着即便多行資料的主鍵相同,它 們還是能夠被正常寫入。
- 能夠在合并分區時删除重複的資料。
- ReplacingMergeTree是以分區為機關删除重複資料的。
- ENGINE = ReplacingMergeTree(ver)
- ver是選填參數,會指定一個UInt*、Date或者DateTime類型的字段作為版本号。
6.3. SummingMergeTree
- 隻需要查詢資料的彙總結果,不關心明細資料,并且資料的彙總條件是預先明确的 。
- SummingMergeTree就是為了應對這類聚合查詢場景而生的。
- 隻能進行SUM計算;對于那些非彙總字段,則會使用第一行資料的取值。
- 照預先定義的條件聚合彙總資料,将同一分組下的多行資料彙總合并成一行。
- 這樣既減少了資料行,又降低了後續彙總查詢的開銷。
- ENGINE = SummingMergeTree() PRIMARY KEY id
6.4. AggregatingMergeTree
- 将需要聚合的資料,預先計算出來,并将結果儲存起來。
- 在後續進行聚合查詢的時候,直接使用結果資料。
-
-- GROUP BY id,city -- UNIQ(code), SUM(value) CREATE TABLE agg_table( id String, city String, code AggregateFunction(uniq,String), value AggregateFunction(sum,UInt32), create_time DateTime )ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY (id,city) PRIMARY KEY id
- AggregatingMergeTree更為常見的應用方式是結合物化視圖使用,将它作為物化視圖的表引擎。
6.5. CollapsingMergeTree
- 是一種通過以增代删的思路,支援行級資料修改和删除的表引擎。它通過定義一個sign标記位字段,記錄資料行的狀态。
- 如果sign标記為1,則表示這是一行有效的資料;如果sign标記為-1,則表示這行資料需要被删除。
- 分區合并時,同一資料分區内,sign标記為1和-1的一組資料會被抵消删除。
- 這種1和-1互相抵消的操作,猶如将一張瓦楞紙折疊了一般。
- ENGINE = CollapsingMergeTree(sign)
- 隻有相同分區内的資料才有可能被折疊。
- 折疊規則
- 如果1比-1的資料多一行,則保留最後一行sign=1的資料。
- 如果**-1比sign=1的資料多一行,則保留第一行sign=-1的資料**。
- 如果1和-1的資料行一樣多,并且最後一行是sign=1,則保留第一行sign=-1和最後一行sign=1的資料。
- 如果1和-1的資料行一樣多,并且最後一行是sign=-1,則什麼也不保留。
- 其餘情況,ClickHouse會列印警告日志,但不會報錯,在這種情形下,查詢結果不可預知。
- 特點
- 折疊資料并不是實時觸發的,和所有其他的MergeTree變種表引擎一樣,這項特性也隻有在分區合并的時候才會展現。
- 是以在分區合并之前,使用者還是會看到舊的資料。解決這個問題的方式有兩種。
- 用optimize TABLE table_name FINAL指令強制分區合并。
- CollapsingMergeTree對于寫入資料的順序有着嚴格要求。
- 先寫入sign=1,再寫入sign=-1,則能夠正常折疊
- 先寫入sign=-1,再寫入sign=1,則不能夠折疊
6.6. VersionedCollapsingMergeTree
- 作用與CollapsingMergeTree完全相同.
- VersionedCollapsingMergeTree對資料的寫入順序沒有要求,在同一個分 區内,任意順序的資料都能夠完成折疊操作。
- ENGINE = VersionedCollapsingMergeTree(sign,ver)
7、常見類型表引擎
7.1. 外部存儲
- 外部存儲表引擎直接從其他的存儲系統讀取資料 .
- 例如直接讀取HDFS的檔案或者MySQL資料庫的表。
- 這些表引擎隻負責中繼資料管理和資料查詢,而它們自身通常并不負責資料的寫入,資料檔案直接由外部系統提供。
7.1.1. HDFS
7.1.2. Mysql
- MySQL表引擎可以與MySQL資料庫中的資料表建立映射,并通過SQL向其發起遠端查詢,包括 SELECT和INSERT
-
- host:port表示MySQL的位址和端口。
- database表示資料庫的名稱。
- table表示需要映射的表名稱。
- user表示MySQL的使用者名。
- password表示MySQL的密碼。
- replace_query預設為0,對應MySQL的REPLACE INTO文法。如果将它設定為1,則會用 REPLACEINTO代替INSERT INTO。
- on_duplicate_clause預設為0,對應MySQL的ON DUPLICATE KEY文法。如果需要使用該設 置,則必須将replace_query設定成0。
CREATE TABLE mysql_dept(
deptno UInt32,
dname String,
loc String
)ENGINE = MySQL('192.168.88.101:3306', 'scott', 'dept', 'root','123456');
SELECT * FROM mysql_dept INSERT INTO TABLE mysql_dept VALUES (50,'幹飯部','207')
-- 目前MySQL表引擎不支援任何UPDATE和DELETE操作
7.1.3. JDBC
- JDBC表引擎不僅可以對接MySQL資料庫,還能夠與PostgreSQL、SQLite和H2資料庫對接。
- JDBC表引擎無法單獨完成所有的工作,它需要依賴名為clickhouse-jdbc-bridge的查詢代理服務。
- clickhouse-jdbc-bridge是一款基于Java語言實作的SQL代理服務,它的項目位址為https://github. com/ClickHouse/clickhouse-jdbc-bridge 。
- clickhouse-jdbc-bridge可以為ClickHouse代理通路其他的資料庫,并自動轉換資料類型。
7.1.4. Kafka
- 目前ClickHouse還不支援恰好一次(Exactly once)的語義,因為這需要應用端與Kafka深度配合才能實作。
ENGINE = Kafka()
SETTINGS
kafka_broker_list = 'host:port,... ',
kafka_topic_list = 'topic1,topic2,...',
kafka_group_name = 'group_name',
kafka_format = 'data_format'[,]
[kafka_row_delimiter = 'delimiter_symbol']
[kafka_schema = '']
[kafka_num_consumers = N]
[kafka_skip_broken_messages = N]
[kafka_commit_every_batch = N]
- 必填參數:
- kafka_broker_list:表示Broker服務的位址清單,多個位址之間使用逗号分隔。
- kafka_topic_list:表示訂閱消息主題的名稱清單,多個主題之間使用逗号分隔。
- kafka_group_name:表示消費組的名稱,表引擎會依據此名稱建立Kafka的消費組。
- kafka_format:表示用于解析消息的資料格式,在消息的發送端,必須按照此格式發送 消息。資料格式必須是ClickHouse提供的格式之一
- 例如TSV、JSONEachRow和CSV等。
- 再次執行SELECT查詢會發現kafka_test資料表空空如也,這是因為Kafka表引擎在執行查詢之後就會删除表内的資料。
7.1.5. File
- File表引擎能夠直接讀取本地檔案的資料,通常被作為一種擴充手段來使用。
- File表引擎的定義參數中,并沒有包含檔案路徑這一項。是以,File表引擎的資料檔案隻能儲存在 config.xml配置中由path指定的路徑下。
- 每張File資料表均由目錄和檔案組成,其中目錄以表的名稱命名,而資料檔案則固定以data.format 命名。
7.2. 記憶體類型
- 将資料全量放在記憶體中,對于表引擎來說是一把雙刃劍:
- 一方面,這意味着擁有較好的查詢性能;
- 另一方面,如果表内裝載的資料量過大,可能會帶來極大的記憶體消耗和負擔。
7.2.1. Memory
- Memory表引擎直接将資料儲存在記憶體中,資料既不會被壓縮也不會被格式轉換,資料在記憶體中儲存的形态與查詢時看到的如出一轍。
- 當ClickHouse服務重新開機的時候,Memory表内的資料會全部丢失。
- 當資料被寫入之後,磁盤上不會建立任何資料檔案。 操作方式。
7.2.2. Set
- Set表引擎是擁有實體存儲的,資料首先會被寫至記憶體,然後被同步到磁盤檔案中。
- 是以當服務重新開機時,它的資料不會丢失,當資料表被重新裝載時,檔案資料會再次被全量加載至記憶體。
- Set表引擎具有去重的能力,在資料寫入的過程中,重複的資料會被自動忽略。
- Set表引擎的存儲結構由兩部分組成,它們分别是:
- [num].bin資料檔案:儲存了所有列字段的資料。其中,num是一個自增id,從1開始。伴随 着每一批資料的寫入(每一次INSERT),都會生成一個新的.bin檔案,num也會随之加1。
- tmp臨時目錄:資料檔案首先會被寫到這個目錄,當一批資料寫入完畢之後,資料檔案會被移出此目錄。
CREATE TABLE set_1 (
id UInt8
)ENGINE = Set()
INSERT INTO TABLE set_1 SELECT number FROM numbers(10)
SELECT arrayJoin([1, 2, 3]) AS a WHERE a IN set_1
7.2.3. Join
- Join表引擎可以說是為JOIN查詢而生的,它等同于将JOIN查詢進行了一層簡單封裝。在Join表引擎的底層實作中,它與Set表引擎共用了大部分的處理邏輯,是以Join和Set表引擎擁有許多相似之處。
- join_strictness:連接配接精度,它決定了JOIN查詢在連接配接資料時所使用的政策,目前支援ALL、 ANY和ASOF三種類型。
- join_type:連接配接類型,它決定了JOIN查詢組合左右兩個資料集合的政策,它們所形成的結果 是交集、并集、笛卡兒積或其他形式,目前支援INNER、OUTER和CROSS三種類型。當 join_type被設定為ANY時,在資料寫入時,join_key重複的資料會被自動忽略。
- join_key:連接配接鍵,它決定了使用哪個列字段進行關聯。
7.3. 日志類型
7.3.1. TinyLog
- TinyLog是日志家族系列中性能最低的表引擎,它的存儲結構由資料檔案和中繼資料兩部分組成。
- 資料檔案是按列獨立存儲的,也就是說每一個列字段都擁有一個與之對應的.bin檔案。
- TinyLog既不支援分區,也沒有.mrk标記檔案 。
- 由于沒有标記檔案,它自然無法支援.bin檔案的并行讀取操作,是以它隻适合在非常簡單的場景下使用。
7.3.2. StripeLog
- StripeLog表引擎的存儲結構由固定的3個檔案組成,它們分别是:
- data.bin:資料檔案,所有的列字段使用同一個檔案儲存,它們的資料都會被寫入data.bin。
- index.mrk:資料标記,儲存了資料在data.bin檔案中的位置資訊。利用資料标記能夠使用多 個線程,以并行的方式讀取data.bin内的壓縮資料塊,進而提升資料查詢的性能。
- sizes.json:中繼資料檔案,記錄了data.bin和index.mrk大小的資訊。
7.3.3. Log
- Log表引擎結合了TinyLog表引擎和StripeLog表引擎的長處,是日志家族系列中性能最高的表引擎。
- Log表引擎的存儲結構由3個部分組成:
- [column].bin:資料檔案,資料檔案按列獨立存儲,每一個列字段都擁有一個與之對應的.bin 檔案。
- marks.mrk:資料标記,統一儲存了資料在各個[column].bin檔案中的位置資訊。利用資料标 記能夠使用多個線程,以并行的方式讀取.bin内的壓縮資料塊,進而提升資料查詢的性能。
- sizes.json:中繼資料檔案,記錄了[column].bin和__marks.mrk大小的資訊。
CREATE TABLE log_1 (
id UInt64,
code UInt64
)ENGINE = Log()
INSERT INTO TABLE log_1 SELECT number,number+1 FROM numbers(200)
7.4. 接口類型
7.4.1. Merge
- 在資料倉庫的設計中,資料按年分表存儲,例如test_table_2018、test_table_2019和 test_table_2020。假如現在需要跨年度查詢這些資料 ?
- Merge表引擎就如同一層使用了門面模式的代理,它本身不存儲任何資料,也不支援資料寫入。
- 它的作用就如其名,即負責合并多個查詢的結果集。
- Merge表引擎可以代理查詢任意數量的資料表,這些查詢會異步且并行執行,并最終合成一個結果集傳回。
- 被代理查詢的資料表被要求處于同一個資料庫内,且擁有相同的表結構,但是它們可以使用不同的表引擎以及不同的分區定義
8、 資料查詢方式
- ClickHouse對于SQL語句的解析是大小寫敏感的,這意味着SELECT a和SELECT A表示的語義是不 相同的。
[WITH expr |(subquery)]
SELECT [DISTINCT] expr
[FROM [db.]table | (subquery) | table_function] [FINAL]
[SAMPLE expr]
[[LEFT] ARRAY JOIN]
[GLOBAL] [ALL|ANY|ASOF] [INNER | CROSS | [LEFT|RIGHT|FULL [OUTER]] ]
JOIN (subquery)|table ON|USING columns_list
[PREWHERE expr]
[WHERE expr]
[GROUP BY expr] [WITH ROLLUP|CUBE|TOTALS]
[HAVING expr]
[ORDER BY expr]
[LIMIT [n[,m]]
[UNION ALL]
[INTO OUTFILE filename]
[FORMAT format]
[LIMIT [offset] n BY columns]
- 方括号包裹的查詢子句表示其為可選項,是以隻有SELECT子句是必須的,
- ClickHouse對于查詢文法的解析也大緻是按照上面各個子句排列的順序進行的。
8.1. With子句
- 支援CTE(Common Table Expression,公共表表達式),以增強查詢語句的表達。
- 在改用CTE的形式後,可以極大地提高語句的可讀性和可維護性。
- 定義變量
- 這些變量能夠在後續的查詢子句中被直接通路。
-
WITH 10 AS start SELECT number FROM system.numbers WHERE number > start LIMIT 5
- 調用函數
- 通路SELECT子句中的列字段,并調用函數做進一步的加工處理。
-
WITH SUM(data_uncompressed_bytes) AS bytes SELECT database , formatReadableSize(bytes) AS format FROM system.columns GROUP BY database ORDER BY bytes DESC
- 定義子查詢
-
-- 子查詢 WITH ( SELECT SUM(data_uncompressed_bytes) FROM system.columns ) AS total_bytes SELECT database , (SUM(data_uncompressed_bytes) / total_bytes) * 100 AS database_disk_usage FROM system.columns GROUP BY database ORDER BY database_disk_usage DESC
-
- 子查詢中重複(嵌套)使用WITH
- 嵌套使用WITH子句
-
WITH ( round(database_disk_usage) ) AS database_disk_usage_v1 SELECT database,database_disk_usage, database_disk_usage_v1 FROM ( -- 嵌套 WITH ( SELECT SUM(data_uncompressed_bytes) FROM system.columns ) AS total_bytes SELECT database , (SUM(data_uncompressed_bytes) / total_bytes) * 100 AS database_disk_usage FROM system.colum GROUP BY database ORDER BY database_disk_usage DESC )
8.2. From子句
- FROM子句表示從何處讀取資料,目前支援如下3種形式
- 從資料表中取數
-
SELECT WatchID FROM hits_v1
-
- 從子查詢中取數
-
SELECT MAX_WatchID FROM (SELECT MAX(WatchID) AS MAX_WatchID FROM hits_v1)
-
- 從表函數中取數
-
SELECT number FROM numbers(5)
-
- 從資料表中取數
- 在ClickHouse中,并沒有資料庫中常見的DUAL虛拟表,取而代之的是system.one。
-
SELECT 1 SELECT 1 FROM system.one
- 在FROM子句後,可以使用Final修飾符。
- 它可以配合CollapsingMergeTree和Versioned-CollapsingMergeTree等表引擎進行查詢操作,以強制在查詢過程中合并
- 但由于Final修飾符會降低查詢性能,是以應該盡可能避免使用它。
8.3. Sample子句
- 能夠實作資料抽樣的功能,使查詢僅傳回采樣資料而不是全部資料,進而有效減少查詢負載。
- SAMPLE子句隻能用于MergeTree系列引擎的資料表,并且要求在CREATE TABLE時聲明SAMPLE BY抽樣表達式
-- Sample Key聲明的表達式必須也包含在主鍵的聲明中
-- Sample Key必須是Int類型,如若不是,ClickHouse在進行CREATE TABLE操作時也不會報
錯
CREATE TABLE hits_v1 (
CounterID UInt64,
EventDate DATE,
UserID UInt64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, intHash32(UserID))
SAMPLE BY intHash32(UserID)
- SAMPLE子句目前支援如下3種用法
- SAMPLE factor
- SAMPLE factor表示按因子系數采樣,其中factor表示采樣因子,它的取值支援0~1之間 的小數。
- 如果factor設定為0或者1,則效果等同于不進行資料采樣。
-
SELECT count() * 10 FROM hits_v1 SAMPLE 0.1 SELECT CounterID, _sample_factor FROM hits_v1 SAMPLE 0.1 LIMIT 2
- SAMPLE rows
- SAMPLE rows表示按樣本數量采樣,其中rows表示至少采樣多少行資料,它的取值必須 是大于1的整數。
- 如果rows的取值大于表内資料的總行數,則效果等于rows=1
-
SELECT count() FROM hits_v1 SAMPLE 10000 SELECT CounterID,_sample_factor FROM hits_v1 SAMPLE 100000 LIMIT 1
- SAMPLE factor OFFSET n
- SAMPLE factor OFFSET n表示按因子系數和偏移量采樣,其中factor表示采樣因子,n 表示偏移多少資料後才開始采樣,它們兩個的取值都是0~1之間的小數。
-
-- 最終的查詢會從資料的0.5處開始,按0.4的系數采樣資料 SELECT CounterID FROM hits_v1 SAMPLE 0.4 OFFSET 0.5 -- 最終的查詢會從資料的二分之一處開始,按0.1的系數采樣資料 SELECT CounterID,_sample_factor FROM hits_v1 SAMPLE 1/10 OFFSET 1/2
- 如果在計算OFFSET偏移量後,按照SAMPLE比例采樣出現了溢出,則資料會被自動截斷
- SAMPLE factor
8.4. Array Join子句
- ARRAY JOIN子句允許在資料表的内部,與數組或嵌套類型的字段進行JOIN操作,進而将一行數組展開為多行。接下來讓我們看看它的基礎用法。
- 一條SELECT語句中,隻能存在一個ARRAY JOIN(使用子查詢除外)。目前支援INNER和LEFT兩種
- INNER ARRAY JOIN 預設
- LEFT ARRAY JOIN 左連接配接
CREATE TABLE query_v1(
title String,
value Array(Int8)
) ENGINE = Log
INSERT INTO query_v1 VALUES ('food', [1,2,3]), ('fruit', [3,4]), ('meat', []);
SELECT title,value FROM query_v1;
8.5. Join 子句
- JOIN子句可以對左右兩張表的資料進行連接配接
- JOIN的文法包含連接配接精度和連接配接類型兩部分
- JOIN查詢還可以根據其執行政策被劃分為本地查詢和遠端查詢。
- 目前支援ALL、ANY和ASOF三種類型。如果不主動聲明,則預設是ALL。
- all 如果左表内的一行資料,在右表中有多行資料與之連接配接比對,則傳回右表中全部連接配接的資料。
- any 如果左表内的一行資料,則僅傳回右表中第一行連接配接的資料。
- asof ASOF是一種模糊連接配接,它允許在連接配接鍵之後追加定義一個模糊連接配接的比對條件asof_column;僅傳回了右表中第一行連接配接比對的資料。
- 連接配接類型
- INNER JOIN表示内連接配接,在查詢時會以左表為基礎逐行周遊資料,
- OUTER JOIN表示外連接配接,它可以進一步細分為左外連接配接(LEFT)、右外連接配接(RIGHT)和全外連接配接(FULL)三種形式。
- CROSS JOIN表示交叉連接配接,它會傳回左表與右表兩個資料集合的笛卡兒積。
- 查詢優化
- 為了能夠優化JOIN查詢性能,首先應該遵循左大右小的原則 ,無論使用的是哪種連接配接方式,右表都會被全部加載到記憶體中與左表進行比較。
- JOIN查詢目前沒有緩存的支援
- 如果是在大量次元屬性補全的查詢場景中,則建議使用字典代替JOIN查詢
- 連接配接查詢的空值是由預設值填充的,這與其他資料庫所采取的政策不同(由Null填充)。
8.6. WHERE與PREWHERE子句
- WHERE子句基于條件表達式來實作資料過濾。如果過濾條件恰好是主鍵字段,則能夠進一步借助索引加速查詢;
- PREWHERE目前隻能用于MergeTree系列的表引擎,它可以看作對WHERE的一種優化,其作用與WHERE相同,均是用來過濾資料。
-
ClickHouse實作了自動優化的功能,會在條件合适的情況下将WHERE替換為PREWHERE。
如果想開啟這項特性,需要将optimize_move_to_prewhere設定為1
8.7. GROUP BY子句
- GROUP BY又稱聚合查詢
-
聚合查詢目前還能配合WITH ROLLUP、WITHCUBE和WITH TOTALS三種修飾符擷取額外的彙總信
息。
8.7.1. WITH ROLLUP
- 按照聚合鍵從右向左上卷資料,基于聚合函數依次生成分組小計和總計。
- 設聚合鍵的個數為n,則最終會生成小計的個數為n+1。
8.7.2. WITH CUBE
- CUBE會像立方體模型一樣,基于聚合鍵之間所有的組合生成小計資訊。如果設聚合鍵的個數為n,則最終小計組合的個數為2的n次方。
8.7.3. WITH TOTALS
- 使用TOTALS修飾符後,會基于聚合函數對所有資料進行總計
8.8. Having子句
- HAVING子句需要與GROUP BY同時出現,不能單獨使用。它能夠在聚合計算之後實作二次過濾資料。
8.9. ORDER BY子句
- ORDER BY子句通過聲明排序鍵來指定查詢資料傳回時的順序。
- ORDER BY在使用時可以定義多個排序鍵,每個排序鍵後需緊跟ASC(升序)或DESC(降序)來确定排列順序。如若不寫,則預設為ASC(升序)。
- 空值處理
-
NULLS LAST
資料的排列順序為其他值(value)→NaN→NULL。
-
NULLS FIRST
資料的排列順序為NULL→NaN→其他值(value)
ORDER BY v1 DESC NULLS FIRST
-
8.10. LIMIT BY子句
- LIMIT BY子句和大家常見的LIMIT所有不同,它運作于ORDER BY之後和LIMIT之前,能夠按照指定分組,最多傳回前n行資料(如果資料少于n行,則按實際數量傳回)
- 常用于TOP N的查詢場景。LIMIT BY的正常文法如下:
LIMIT n BY express
8.11. LIMIT子句
- LIMIT子句用于傳回指定的前n行資料,常用于分頁場景
- LIMIT n
SELECT number FROM system.numbers LIMIT 10
- LIMIT n OFFSET m
SELECT number FROM system.numbers LIMIT 10 OFFSET 5
- LIMIT m,n
SELECT number FROM system.numbers LIMIT 5 ,10