在大部分的DBMS中,數據庫本質上就是一個由各種子目錄和文件組成的文件目錄,clickhouse當然也不例外。clickhouse默認數據目錄在/var/lib/clickhouse/data目錄中。所有的數據庫都會在該目錄中創建一個子文件夾。下圖展示了clickhouse對數據文件的組織。
每一個數據庫都會在clickhouse的data目錄中創建一個子目錄,clickhouse默認攜帶default和system兩個數據庫。default顧名思義就是默認數據庫,system是存儲clickhouse服務器相關信息的數據庫,例如連接數、資源占用等。
分區目錄
分區目錄下的子目錄和文件的含義如下:
目錄名 | 類型 | 說明 |
202103_1_10_2 | 目錄 | 分區目錄一個或多個,由於分區+LSM生成的 |
detached | 目錄 | 通過DETACH語句卸載后的表分區存放位置 |
format_version.txt | 文本文件 | 純文本,記錄存儲的格式 |
分區目錄構成
分區目錄的構成,按照 分區ID_最小數據塊編號_最大數據塊編號_層級構成。在本例中,分區ID是202103,最小數據塊編號是1,最大數據庫編號是10,層級是2。數據塊編號從1開始自增,新創建的數據庫最大和最小編號相同,當發生合並時會將其修改為合並的數據塊編號。同時每次合並都會將層級增加1。
分區ID由用戶在創建表時制定,允許用戶創建多個分區鍵,每個分區鍵之間用‘-’相連。在本例中只使用了一個分區鍵,即時間字段,按照年月分區。分區的好處在於提高並發度和加速部分查詢。
數據目錄
進入分區目錄后,就能看到數據真實存儲的數據目錄的結構了。
columns.txt
該文件是一個文本文件,存儲了表結構信息,可以用文本編輯打開。
count.txt
該文件也是一個文本文件,存儲了該分區下的行數。可以用文本文件打開。在用戶執行select count(*) from xxx時本質上就是直接返回了該文件的內容,而不需要遍歷數據。因此clickhouse的count(*)的速度非常快。
同時,這邊也對比一下MySQL和PostgreSQL的實現,在上述兩個關系型數據庫中,其常用的存儲引擎,都沒有使用clickhouse的這種方案。讀者們能否回答出為了MySQL或pg要舍棄簡單的方案而使用遍歷么?
這個問題的答案是由於事務的可見性,MySQL和pg都是用MVCC機制的事務控制技術,這意味着對於不同事務中執行select count(*)的結果是不同的,對於A事務中執行的insert或delete,對於本事務中是可見的,也就是說在本事務中執行的count是計算了insert和delete影響的。而對於B事務,在A事務提交前,是不能看到A事務中對數據的操作的。因此AB兩個事務中執行的count后的結果可能是不同的。如果使用clickhouse的方案,就無法實現上述需求。而clickhouse則不需要支持事務,因此使用了相對簡單的方案。
primary.idx
主鍵索引
checksums.txt
二進制文件
default_compression_codec.txt
新版本增加的一個文件,在舊版本時無。該文件是一個文本文件,存儲了數據文件中使用的壓縮編碼器。clickhouse提供了多種壓縮算法供用戶選擇,默認使用LZ4。
[column].mrk3
列的標記文件
[column].bin
真正存儲數據的數據文件。每一列都會生成一個單獨的bin文件。
skp_idx_[column].idx
跳數索引,在使用了二級索引時會生成,否則這不生成。
skp_idx_[column].mrk
跳數索引標記文件,在使用了二級索引時會生成。否則這不生成
數據組織
如何讀取bin文件。由於bin文件是二進制文件,在讀取時需要借助工具,無法使用文本文件進行讀取。在windows操作系統下建議使用winhex,mac系統推薦hex friend。
數據文件結構
上圖展示了一個bin文件的結構。bin文件使用小端字節序存儲。bin文件中按block為單位排列數據,每個block文件有16字節校驗和,1字節壓縮方式,4字節壓縮后大小和4字節的壓縮前大小組成。每個block起始地址由如下公式確定:
- offset(n)=offset(n-1)+25+壓縮后大小 (n>=2)
- offset(1)=0
校驗和
前16為檢驗和區域用於快速驗證數據是否完整。
壓縮方式
默認為0x82。clickhouse共支持4種壓縮方式,分別為LZ4(0x82)、ZSTD(0x90)、Multiple(0x91)、Delta(0x92)。
壓縮后大小
存儲在data區域的數據的大小。需要依據此大小計算下一個BLOCK的偏移量。
壓縮前大小
data區域存儲的數據在壓縮前的大小。可以依據此計算壓縮比。
data區
data區存儲數據,大小為頭信息第18~21字節表示的大小。拿到data區數據后,由於是壓縮后的,因此無法直接識別,需要按照壓縮方式進行解壓縮后,才能識別。