近年來,隨著大數(shù)據(jù)分析技術(shù)的普及和物聯(lián)網(wǎng)產(chǎn)業(yè)的興起,越來越多的企業(yè)開始重視海量數(shù)據(jù)的收集和分析處理活動,希望從龐大的數(shù)據(jù)資料中挖掘出高價(jià)值的信息和洞見。而在數(shù)據(jù)規(guī)模快速膨脹的同時(shí),企業(yè)對數(shù)據(jù)處理平臺的軟硬件基礎(chǔ)設(shè)施也提出了更高的要求,并在這一領(lǐng)域催生了很多高水平的前沿技術(shù)變革。
在這樣的趨勢下,由俄羅斯 Yandex 開發(fā)的一款名為 Clickhouse 的數(shù)據(jù)庫產(chǎn)品就在眾多競爭者中脫穎而出,憑借十億乃至百億行的數(shù)據(jù)規(guī)模下依舊具備秒級返回能力的卓越性能,贏得了全球眾多大廠的青睞。Clickhouse 究竟有怎樣的獨(dú)門絕技,如何做到如此強(qiáng)悍的性能表現(xiàn),實(shí)踐中又是如何在主流云平臺上部署和優(yōu)化的?9 月 22 日,來自京東智聯(lián)云云產(chǎn)品研發(fā)部的架構(gòu)師王向飛老師做客 InfoQ,介紹了 Clickhouse 數(shù)據(jù)庫在京東智聯(lián)云的落地應(yīng)用與優(yōu)化改進(jìn)經(jīng)歷,為想要深入了解 Clickhouse 的小伙伴們送上了一堂干貨滿滿的技術(shù)分享課程。
本文總結(jié)自王向飛老師的在線公開課:《Clickhouse 在京東智聯(lián)云的大規(guī)模應(yīng)用和架構(gòu)改良》。
根據(jù)數(shù)據(jù)庫處理的業(yè)務(wù)數(shù)據(jù)量,以及處理數(shù)據(jù)的不同方式,人們把數(shù)據(jù)庫分為 OLTP 和 OLAP 兩大類型。由俄羅斯 Yandex 搜索引擎公司開發(fā)并開源的 Clickhouse 數(shù)據(jù)庫,其初始定位就是 Yandex 內(nèi)部的分析型數(shù)據(jù)庫,符合 OLAP 類型數(shù)據(jù)庫的實(shí)現(xiàn)特點(diǎn)。Clickhouse 的性能超過了很多流行的商業(yè)數(shù)據(jù)庫,已經(jīng)得到了包括 CloudFlare、Spotify、阿里云、騰訊云、京東智聯(lián)云、今日頭條、攜程等諸多頭部大廠的采用。
OLTP vs OLAP
常見的 Oracle、MySQL 等數(shù)據(jù)庫都屬于 OLTP 類型,也就是 On-Line Transaction Processing,聯(lián)機(jī)事務(wù)處理。OLTP 數(shù)據(jù)庫處理請求和數(shù)據(jù)時(shí)對延遲要求很高,并且要保證數(shù)據(jù)的完整性和一致性。此類數(shù)據(jù)庫是面向最終客戶的,需要具備 7x24 不間斷服務(wù)能力。
OLAP 的含義是聯(lián)機(jī)分析數(shù)據(jù)處理,這種數(shù)據(jù)庫需要存儲海量、但很少更新修改的數(shù)據(jù),主要用于多維度歷史數(shù)據(jù)分析統(tǒng)計(jì)目的。出于這種需求,OLAP 數(shù)據(jù)庫需要確保足夠高的查詢效率,至少 90% 的請求要在很短的時(shí)間內(nèi)返回。
另一方面,OLAP 數(shù)據(jù)庫并不是直接面對最終客戶,而是更關(guān)注數(shù)據(jù)吞吐,要求海量數(shù)據(jù)盡快持久化,為業(yè)務(wù)決策、戰(zhàn)略定位和分析、個(gè)性化推薦等任務(wù)提供分析統(tǒng)計(jì)能力,所以這類數(shù)據(jù)庫中的查詢一般都是較低頻的。
Clickhouse 的關(guān)鍵特性:
列式存儲基于 OLAP 數(shù)據(jù)庫的特點(diǎn),Clickhouse 采用了基于列的數(shù)據(jù)存儲引擎。傳統(tǒng)的行式數(shù)據(jù)庫在存儲信息時(shí),是在數(shù)據(jù)庫中按順序逐個(gè)記錄的。以用戶注冊信息為例,行式數(shù)據(jù)庫會將每個(gè)用戶的姓名、職業(yè)、年齡等數(shù)據(jù)依次記錄下來。當(dāng)業(yè)務(wù)需要查找注冊用戶的職業(yè)或年齡分布時(shí),數(shù)據(jù)庫需要打開所有存儲用戶注冊信息的文件,遍歷全部數(shù)據(jù)行,依次挑出所有職業(yè)和年齡信息進(jìn)行匯總。使用這種方式,查詢遍歷的數(shù)據(jù)往往遠(yuǎn)大于所需的數(shù)據(jù)大小,對 IO 能力會造成嚴(yán)重浪費(fèi)。
相比之下,列式存儲會將數(shù)據(jù)分為多個(gè)屬性列,例如用戶注冊信息分為職業(yè)、年齡等多個(gè)屬性,并按這些屬性列分為多個(gè)文件分別存儲。這樣當(dāng)查詢需要獲取其中某些屬性的數(shù)據(jù)時(shí),只需查找對應(yīng)文件即可完成,大大節(jié)約了 IO 需求。
以一個(gè)包含 1 億條數(shù)據(jù)的測試表為例,一個(gè)簡單的 count 查詢在 MySQL 上需要兩分多鐘,而在列式存儲的 Clickhouse 數(shù)據(jù)庫上僅用不足 1 秒就返回了結(jié)果。那么,Clickhouse 具體都使用了哪些技術(shù)來實(shí)現(xiàn)如此高的效率提升呢?
B+Tree vs MergeTree
在深入研究 Clickhouse 之前,我們首先以傳統(tǒng)的 MySQL InnoDB 的存儲格式來做對比。
從 InnoDB 的邏輯結(jié)構(gòu)圖可以看到,在 InnoDB 中所有數(shù)據(jù)會被放在表空間內(nèi)。表空間可以看作是 InnoDB 的邏輯最高層,由多個(gè)段組成,段又分為數(shù)據(jù)段、索引段。數(shù)據(jù)生成時(shí)按順序?qū)懭霐?shù)據(jù)段,隨著數(shù)據(jù)記錄的增多,InnoDB 會將一些主鍵值放到索引段內(nèi)以實(shí)現(xiàn)快速定位。
隨著數(shù)據(jù)量不斷增多,數(shù)據(jù)庫形成了名為 B+Tree 的樹狀結(jié)構(gòu)。這個(gè)樹有層級結(jié)構(gòu),會橫向生長,其查詢的復(fù)雜度取決于樹的高度。B+Tree 的數(shù)據(jù)節(jié)點(diǎn)一般存儲主鍵值,根據(jù)主鍵查找時(shí)可以通過葉子節(jié)點(diǎn)大概定位數(shù)據(jù)頁,之后直接讀取數(shù)據(jù)頁即可。
而 Clickhouse 的數(shù)據(jù)架構(gòu)類似關(guān)系型數(shù)據(jù)庫,其中包括了解析器,主要負(fù)責(zé)將 SQL 語句通過詞法分析、語法分析等轉(zhuǎn)換成計(jì)算機(jī)可讀的抽象語法樹。另外還有優(yōu)化器,邏輯優(yōu)化負(fù)責(zé)優(yōu)化抽象語法樹的邏輯,比如簡化一些長難運(yùn)算表達(dá)式,做一些語義優(yōu)化等。物理優(yōu)化則負(fù)責(zé)生成可以直接執(zhí)行的物理執(zhí)行計(jì)劃,指導(dǎo)數(shù)據(jù)庫管理系統(tǒng)如何獲取數(shù)據(jù)表、如何進(jìn)行數(shù)據(jù) join、排序等等。
Clickhouse 的物理執(zhí)行計(jì)劃可認(rèn)為是一個(gè)數(shù)據(jù)流圖,也就是數(shù)據(jù)的有向無環(huán)圖。在這個(gè)圖里,數(shù)據(jù)從一個(gè)管道傳到另一個(gè)管道,也就是從一個(gè)操作符傳到另一個(gè)操作符。查詢執(zhí)行器是用來執(zhí)行計(jì)劃的引擎,它會從存儲引擎中取出數(shù)據(jù),并返回給客戶端。
如上圖,Clickhouse 在啟動時(shí)加載配置信息,然后根據(jù)不同的解析協(xié)議監(jiān)聽不同的服務(wù)端口。客戶端發(fā)送來 SQL 請求后,首先它會對 SQL 進(jìn)行語法解析,然后生成抽象語法樹,并進(jìn)行一系列的邏輯優(yōu)化、物理優(yōu)化,生成執(zhí)行計(jì)劃。接下來由不同的執(zhí)行器根據(jù) SQL 請求來將執(zhí)行計(jì)劃分發(fā)到本地或遠(yuǎn)端的存儲引擎,從存儲引擎中取出數(shù)據(jù)。數(shù)據(jù)經(jīng)過一系列的計(jì)算加工后返回給客戶端,客戶端就可以輸出緩沖區(qū)讀取查詢結(jié)果。
MergeTree 存儲過程
相比 InnoDB 使用的 B+Tree,Clickhouse 使用的是 MergeTree 存儲引擎來存儲數(shù)據(jù)。這里以一個(gè) Clickhouse 表為例:
本例中,我們根據(jù)出生日期做一個(gè)數(shù)據(jù)分區(qū),主鍵選用用戶的名字,并設(shè)置 SETTINGS index_granularity=3。表建成后插入 10 條記錄,分為 2001 年 3 月和 2001 年 2 月兩個(gè)數(shù)據(jù)區(qū)間。表建完、數(shù)據(jù)寫完以后,Clickhouse 默認(rèn)會在數(shù)據(jù)文件存放路徑下建一個(gè)相應(yīng)的表名:
這里可以看到,10 條數(shù)據(jù)分了兩個(gè)文件夾來存儲。文件夾命名時(shí),其第一部分是分區(qū)鍵,也就是出生日期;1_1(2_2)代表每個(gè)數(shù)據(jù)分區(qū)內(nèi)數(shù)據(jù)塊最小塊和最大塊的編號。最后的數(shù)字 0 代表合并層級。
上圖是 MergeTree 中對 Data part 進(jìn)行元數(shù)據(jù)管理的結(jié)構(gòu)體。其中,partition id 代表數(shù)據(jù)所處的分區(qū) id;min block、max block 代表數(shù)據(jù)寫入的版本信息——用戶每次批量寫的數(shù)據(jù)都會生成一個(gè) Data part,同一批寫入的數(shù)據(jù)會被標(biāo)記為唯一的 block number。MergeTree 存儲引擎后臺會定期通過異步任務(wù)合并數(shù)據(jù),且只會合并位于同一個(gè)數(shù)據(jù)分區(qū)內(nèi)的數(shù)據(jù),還要求 min block 和 max block 數(shù)據(jù)區(qū)間必須是連續(xù)非重合的。
第四個(gè) level 字段默認(rèn)新插入的數(shù)據(jù)都是 0,之后會隨著合并次數(shù)的增加在原來的基礎(chǔ)上依次增大。下面的 mutation 字段在數(shù)據(jù)更正時(shí)使用。如果要進(jìn)行數(shù)據(jù)的更正操作,Clickhouse 會默認(rèn)給 mutation 字段進(jìn)行標(biāo)記和更新。
雖然測試數(shù)據(jù)只有一張表 10 條數(shù)據(jù),但它會在磁盤目錄上生成大量文件。具體來說,Clickhouse 默認(rèn)每一個(gè)列生成一個(gè)文件,默認(rèn)數(shù)據(jù)文件放在 bin 文件里。每一個(gè)數(shù)據(jù)分析目錄下生成一個(gè) count 文件,記錄分區(qū)里有多少行數(shù)據(jù)。
本例中,建表時(shí)設(shè)置的 SETTINGS index_granularity 設(shè)為 3。插完數(shù)據(jù)以后觀察主鍵索引,可以發(fā)現(xiàn)它會把主鍵以每 3 條記錄為一個(gè)區(qū)間,將主鍵信息存儲在 primary.idx 里。
結(jié)合前文例子來看數(shù)據(jù)全景。假設(shè)下面綠顏色的就是要寫入的一批數(shù)據(jù),存放用戶的名字;假設(shè)每個(gè)名字占用 4 個(gè)字節(jié),可以看到綠顏色上邊有一個(gè) granule,寫的是 8192。指定 granule 是 8192 之后,數(shù)據(jù)在寫入時(shí)會放到一個(gè)具有緩沖區(qū)的 OutPort 流中,按照一個(gè) granule 一個(gè) granule 來寫;寫完第一個(gè) granule,當(dāng)發(fā)現(xiàn)這個(gè)緩沖區(qū)內(nèi)數(shù)據(jù)大小超過 64KB,這時(shí)就會把數(shù)據(jù)進(jìn)行壓縮落盤,放在下邊的粉紅色文件塊里。落數(shù)據(jù)塊時(shí)會先寫一個(gè)文件頭,文件頭由三部分組成,如上圖所示。
第二段 8192 的數(shù)據(jù),壓縮完之后數(shù)據(jù)塊可能比較長一些;可以發(fā)現(xiàn),數(shù)據(jù)每次寫入就會產(chǎn)生兩個(gè)文件。一個(gè)是 bin 文件,也就是壓縮后的數(shù)據(jù)文件。另一個(gè)文件就是主鍵 index 文件。但這樣以來,在數(shù)據(jù)查詢時(shí)不知道數(shù)據(jù)究竟在數(shù)據(jù)文件里的哪一塊,不知道該怎么拆分 bin 文件,如果把整個(gè) bin 文件都加載內(nèi)存以后掃描,效率是會很差的。為了解決這個(gè)尷尬的問題 Clickhouse 引入了 mrk 文件。寫數(shù)據(jù)文件的時(shí)候會把 bin 文件頭信息寫到 mrk 文件里。比如說第一塊數(shù)據(jù)寫完之后,會把起始位置、解壓縮后的位置、解壓縮前的位置放在 mrk 這個(gè)文件塊里, 作為一行記錄。查詢時(shí)直接根據(jù)主鍵 index 記錄的偏移量找到對應(yīng)的 mrk 記錄的某段數(shù)據(jù)的起始位置,之后讀取數(shù)據(jù)即可。
MergeTree 也有一些異步任務(wù)處理,主要有三部分:首先是定期把一些有問題的、提交失敗的、或?qū)懯〉臄?shù)據(jù)文件清理掉;然后是定期把一些比較瑣碎的插入語句生成的小文件塊合并為大的文件塊;還有偶爾有一些更正,例如數(shù)據(jù)的更新刪除生成的臨時(shí)文件,MergeTree 也會把對應(yīng)的數(shù)據(jù)文件匯總成一個(gè)比較大的 Part。
智聯(lián)云選擇 K8s 部署的原因
京東智聯(lián)云基于現(xiàn)有的云平臺部署 Clickhouse 時(shí),是基于 K8s 團(tuán)隊(duì)提供的強(qiáng)大運(yùn)維調(diào)度平臺來實(shí)現(xiàn)的。之所以選擇基于 K8s 來部署有幾方面原因:
- 首先,K8s 可以屏蔽底層的環(huán)境差異,使用戶無需再具體關(guān)注主機(jī)網(wǎng)絡(luò)、存儲、API 接口等變化,只需將精力集中到數(shù)據(jù)庫管理開發(fā)任務(wù)上。
- 其次,構(gòu)建這個(gè) JCHDB 平臺并不只是為京東智聯(lián)云內(nèi)部使用。這個(gè)平臺構(gòu)建完成后,不僅可以在公有云上給客戶提供服務(wù),并且在私有化部署或者跨云部署時(shí),都可以完全不用對架構(gòu)做任何修改直接部署。
智聯(lián)云部署 Clickhouse 的流程
數(shù)據(jù)庫系統(tǒng)是一個(gè)比較復(fù)雜的有狀態(tài)業(yè)務(wù)系統(tǒng),分片跟副本之間有狀態(tài)關(guān)系,這時(shí)候就需要維護(hù) Pod 與 Pod 之間的關(guān)系,K8s 為此提供了 operator 功能。京東智聯(lián)云二次開發(fā)了開源的 Clickhouse operator,豐富了 operator 的 API 功能,并且為安全生產(chǎn)的需要給它打上一些額外的標(biāo)簽,控制 Pod 調(diào)度的親和性,防止主從副本落到同一個(gè)物理機(jī)上。
這個(gè) operator 開發(fā)部署完畢,安裝到 K8s 以后,K8s 就有了管理調(diào)度 Clickhouse 狀態(tài)的能力。在它的外部會借助 helm 系統(tǒng),將提前定義好的一些表單發(fā)送給 K8s,由 K8s 來根據(jù)表單里定義的這些參數(shù)來創(chuàng)建需要的實(shí)例。
除了創(chuàng)建最基礎(chǔ)的 Clickhouse,如果需要有復(fù)制關(guān)系引擎,它也會一同創(chuàng)建 Zookeeper;同時(shí)為了豐富監(jiān)控能力,便于 DBA 進(jìn)行服務(wù)器的運(yùn)維監(jiān)控,它還會創(chuàng)建一個(gè) Promethus,還有可視化的 Grafana。這樣它就可以直接在 VPC 里通過 Grafana 來監(jiān)控?cái)?shù)據(jù)庫的運(yùn)維狀態(tài)。
這個(gè)服務(wù)還會創(chuàng)建一個(gè)綁定 headless service ip 地址的域名,用戶可以通過這個(gè)數(shù)據(jù)庫域名直接連到這一套 Clickhouse 系統(tǒng)上。由上圖右可見,這個(gè) Pod 的底層存儲使用了京東的云硬盤,它會在建 Pod 時(shí)申請一個(gè) PVC 控制器,PVC 控制器會綁定京東云硬盤。這樣就形成了計(jì)算與存儲分離的架構(gòu),可以進(jìn)一步提升計(jì)算能力。
京東智聯(lián)云目前在高性能、高可用和可擴(kuò)展層面上都有自己的特色:
- 高性能方面,智聯(lián)云采用最新一代的云主機(jī),CPU 最大可以支持 64 核心,單個(gè) Pod 最大可以擴(kuò)展到 512G 內(nèi)存。
- 高可用方面,智聯(lián)云借助 K8s 調(diào)度管理平臺,發(fā)現(xiàn)有 Pod 不可用時(shí) K8s 會自動將這個(gè) Pod 剔除。同時(shí)平臺會新建一個(gè) Pod,基于 StatefulSet 機(jī)制將被刪除的 Pod 所綁定的云盤掛到新 Pod 上。這樣如果有實(shí)例由于某些硬件原因出現(xiàn)問題,可以在分或秒級完成實(shí)例替換。
- 可擴(kuò)展方面,智聯(lián)云基于云主機(jī)、云硬盤的一些基礎(chǔ)組件,提供了一些靈活的擴(kuò)展接口,可以直接在原地?cái)U(kuò)容。智聯(lián)云支持熱擴(kuò)容,可以在不影響用戶使用的前提下,在分鐘級甚至秒級就可以完成 CPU 數(shù)量、內(nèi)存容量或磁盤空間的擴(kuò)容。
京東智聯(lián)云還提供了完善的監(jiān)控體系,可以幫助 DBA 更好地觀察的數(shù)據(jù)庫的運(yùn)行狀況。平臺不但提供了 service 級別的數(shù)據(jù)庫監(jiān)控,同時(shí)還能把所有 Pod 所使用的磁盤空間、CPU、內(nèi)存都展示給用戶。有了這些信息,用戶可以更加直觀地觀察到每一個(gè) Pod 的壓力分布情況,進(jìn)而方便靈活地調(diào)整數(shù)據(jù)壓力,避免某個(gè) Pod 出現(xiàn)數(shù)據(jù)瓶頸。
根據(jù)這些監(jiān)控的目標(biāo),用戶可以靈活地定義告警信息。智聯(lián)云支持多維度數(shù)據(jù)告警,可以通過郵件、短信、微信等形式告警。
Clickhouse 自身具備強(qiáng)大的數(shù)據(jù)處理能力,還能很好地兼容 SQL 語句。但在實(shí)際設(shè)計(jì)和使用過程中,不能僅僅把它當(dāng)成一個(gè)傳統(tǒng)關(guān)系型數(shù)據(jù)庫的增強(qiáng)替代品,這樣可能會限制 Clickhouse 的潛力發(fā)揮。企業(yè)需要對傳統(tǒng)的數(shù)據(jù)倉庫、設(shè)計(jì)理念,以及數(shù)據(jù)上下游的流轉(zhuǎn)方式做出改進(jìn),發(fā)揮想象力和創(chuàng)造力,更好地利用 Clickhouse 的列式存儲、并行計(jì)算等數(shù)據(jù)能力,為數(shù)據(jù)業(yè)務(wù)創(chuàng)造更大價(jià)值。
原文作者:京東智聯(lián)云技術(shù)新知
原文地址:https://segmentfault.com/a/1190000032789022