背景
隨著互聯網的快速發展和互聯網+物聯網的場景不斷增加,信息數據量正在呈幾何式的爆發。這些海量數據決定著企業的未來發展。這些數據中,有用的價值數據就需要數據分析師或分析系統去分析。
但是使用一般的工具己滿足不了和承受不住龐大的數據帶來的壓力。近兩年針對大數據的開發工具開啟了開源大潮,為大數據開發者提供了多個選擇,但是也增加了開發者選擇合適的工具的難度。
尤其是新入行的開發者,會增加很大的學習成本,框架的多樣化和復雜度成了很大的難題。有時候會需要各種框架、工具、中間件、平臺等整合到一起才能完成數據分析。
因此,大數據分析平臺簡單化和統一化成了開發者剛需。
什么是ClickHouse?
ClickHouse是一個用于聯機分析(OLAP)的列式數據庫管理系統(DBMS)。我們可能都聽過clickhouse速度很快,也聽說過它不能處理高并發等各種特性,下面我們就從它的工作原理來分析下這些結論到底對不對,以及為什么會是這樣。 要想弄清楚ClickHouse做查詢分析那么快的原因,咱們可以反客為主,先想想自己設計一款OLAP數據庫的核心技術應該有哪些?然后我們再來看看ClickHouse是如何實現如何工作的。 我們先看兩條查詢語句場景一:select username, number from user where id = 1;場景二:select department, avg(age) from student group by department;第一種場景: 如果數據量小,并且數據是結構化的,使用MySQL去存儲即可; 如果數據量大,不管是不是結構化的,可以轉成key-value的存儲,使用 HBase,Cassandra等來解決。 第二種場景: 如果數據量小,并且數據是結構化的,使用MySQL去存儲即可; 如果數據量大,不管是不是結構化的,設計一個專門用來做分析的存儲計算引擎解決分析的低效率問題。
一、如何設計一個oltp數據庫?
- 內存 + 磁盤:保證處理效率,也保證數據安全
- 內存:必須經過設計,內存具備優秀的數據結構,保證基本的讀寫高效,甚至為了不同的需求,可以讓讀寫效率傾斜。
- 磁盤:數據必須存放在磁盤,保證數據安全。磁盤數據文件必須經過精心設計,保證掃描磁盤數據文件的高效率
- 數據排序:在海量數據中要想保證低延時的隨機讀寫操作,數據最好是排序的
- 范圍分區:當數據排序之后,可以進行范圍分區,來平攤負載,讓多臺服務器聯合起來對外提供服務
- 跳表:基于數據排序+范圍分區構建索引表,形成跳表的拓撲結構,方便用戶操作時快速定位數據分區的位置
-
LSM-Tree存儲引擎:把隨機寫變成順序追加,在通過定期合并的方式來合并數據,去除無效數據,從而實現數據的刪除和修改。image
海量數據中,如果進行高效率的查詢的核心思想:設計一種架構,能夠快速把待搜尋的數據范圍降低到原來的1/n,然后再結合索引或者熱點數據放在內存等思路,就能實現高效率的查詢了。 那么一個專門用來做OLAP分析的存儲引擎該如何設計呢?如何在海量數據中,針對大量數據進行查詢分析呢?一些常見的方案和手段如下:
- 列式存儲 + 字段類型統一
- 列裁剪
- 數據排序
- 數據分區分片 + 分布式查詢
- 預聚合
- 利用CPU特性:向量化引擎,操作系統必須支持
- 主鍵索引+二級索引+位圖索引+布隆索等
- 支持近似計算pv
- 定制引擎:多樣化的存儲引擎滿足不同場景的特定需要
- 多樣化算法選擇:Volnitsky高效字符串搜索算法和HyperLogLog基于概率高效去重算法
總結一下:單條記錄的增刪改等操作,通過數據的橫向劃分,做到數據操作的快速定位,在海量數據查詢分析中,一般就是針對某些列做分析,既然并不是全部列,那么把數據做縱向切分把表中的數據按照列來單獨存儲,那么在做分析的時候,同樣可以快速把待查詢分析的數據總量降低到原來表的1/n,同樣提高效率。
二、總體介紹:
ClickHouse是一個用于聯機分析(OLAP)的列式數據庫管理系統(DBMS)。來自于2011 年在納斯達克上市的俄羅斯本土搜索引擎企業Yandex公司,誕生之初就是為了服務Yandex公司自家的Web流量分析產品Yandex.Metrica,后來經過演變,逐漸形成為現在的 ClickHouse,全稱是:Click Stream,Data WareHouse ClickHouse.
ClickHouse具有ROLAP、在線實時查詢、完整的DBMS功能支持、列式存儲、不需要任何數據預處理、支持批量更新、擁有非常完善的SQL支持和函數、支持高可用、不依賴 Hadoop復雜生態、開箱即用等許多特點。
在1億數據集體量的情況下,ClickHouse的平均響應速度是Vertica的2.63倍、InfiniDB的17倍、MonetDB的27倍、Hive的126倍、MySQL的429倍以及Greenplum的10倍。
詳細的測試結果可以查閱:https://clickhouse.tech/benchmark/dbms/。
ClickHouse非常適用于商業智能領域(也就是我們所說的BI領域),除此之外,它也能夠被廣泛應用于廣告流量、Web、App流量、電信、金融、電子商務、信息安全、網絡游戲、物聯網等眾多其他領域。
ClickHouse是近年來備受關注的開源列式數據庫,主要用于數據分析(OLAP)領域。目前國內社區火熱,各個大廠紛紛跟進大規模使用:
- 今日頭條內部用ClickHouse來做用戶行為分析,內部一共幾千個ClickHouse節點,單集群最大1200節點,總數據量幾十PB,日增原始數據300TB左右。
- 騰訊內部用ClickHouse做游戲數據分析,并且為之建立了一整套監控運維體系。
- 攜程內部從18年7月份開始接入試用,目前80%的業務都跑在ClickHouse上。每天數據增量十多億,近百萬次查詢請求。
- 快手內部也在使用ClickHouse,存儲總量大約10PB,每天新增200TB,90%查詢小于3S。
三:ClickHouse表引擎介紹
表引擎在ClickHouse中的作用十分關鍵,直接決定了數據如何存儲和讀取、是否支持并發讀寫、是否支持index、支持的query種類、是否支持主備復制等。
- 數據的存儲方式和位置,寫到哪里以及從哪里讀取數據
- 支持哪些查詢以及如何支持。
- 并發數據訪問。
- 索引的使用(如果存在)。
- 是否可以執行多線程請求。
- 數據復制參數
具體可看官網:https://clickhouse.tech/docs/zh/engines/table-engines/
關于ClickHouse的底層引擎,其實可以分為數據庫引擎和表引擎兩種。在此,我們重點關注表引擎。
關于庫引擎,簡單總結一下:ClickHouse也支持在創建庫的時候,指定庫引擎,目前支持5種,分別是:Ordinary,Dictionary,Memory,Lazy,MySQL,其實Ordinary 是默認庫引擎,在此類型庫引擎下,可以使用任意類型的表引擎。5種庫引擎說明:
- Ordinary引擎:默認引擎,如果不指定數據庫引擎創建的就是Ordinary數據庫
- Dictionary引擎:此數據庫會自動為所有數據字典創建表
- Memory引擎:所有數據只會保存在內存中,服務重啟數據消失,該數據庫引擎只能夠創建Memory引擎表
- MySQL引擎:改引擎會自動拉取遠端MySQL中的數據,并在該庫下創建 MySQL表引擎的數據表
- Lazy延時引擎:在距最近一次訪問間隔expiration_time_in_seconds時間段內,將表保存在內存中,僅適用于Log引擎表
ClickHouse的表引擎提供了四個系列(Log、MergeTree、Integration、Special)大約28種表引擎,各有各的用途。
比如Log系列用來做小表數據分析,MergeTree系列用來做大數據量分析,而Integration系列則多用于外表數據集成。Log、Special、Integration系列的表引擎相對來說,應用場景有限,功能簡單,應用特殊用途,MergeTree系列表引擎又和兩種特殊表引擎(Replicated,Distributed)正交形成多種具備不同功能的MergeTree表引擎。
這是ClickHouse的表引擎系列家譜:
MergeTree作為家族中最基礎的表引擎,提供了主鍵索引、數據分區、數據副本和數據采樣等基本能力,而家族中其他的表引擎則在 MergeTree 的基礎之上各有所長:
四:MergeTree 引擎工作機制詳解
MergeTree系列是官方主推的存儲引擎,支持幾乎所有ClickHouse核心功能,該系列中,常用的表引擎有:MergeTree、ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree、SummingMergeTree、AggregatingMergeTree等。學習好MergeTree表引擎的工作機制,是應用好ClickHouse的最基本基礎。
4.1 關于表引擎類型
第一:MergeTree表引擎主要用于海量數據分析,支持數據分區、存儲有序、主鍵索引、稀疏索引、數據TTL等。MergeTree支持所有ClickHouse SQL語法,但是有些功能與 MySQL并不一致,比如在MergeTree中主鍵并不用于去重。
第二:為了解決MergeTree相同主鍵無法去重的問題,ClickHouse提供了ReplacingMergeTree引擎,用來做去重。ReplacingMergeTree確保數據最終被去重,但是無法保證查詢過程中主鍵不重復。因為相同主鍵的數據可能被shard到不同的節點,但是 compaction只能在一個節點中進行,而且optimize的時機也不確定。
第三:CollapsingMergeTree引擎要求在建表語句中指定一個標記列Sign(插入的時候指定為1,刪除的時候指定為-1),后臺Compaction時會將主鍵相同、Sign相反的行進行折疊,也即刪除。來消除ReplacingMergeTree的限制。
第四:為了解決CollapsingMergeTree亂序寫入情況下無法正常折疊問題,VersionedCollapsingMergeTree表引擎在建表語句中新增了一列Version,用于在亂序情況下記錄狀態行與取消行的對應關系。主鍵相同,且Version相同、Sign相反的行,在 Compaction時會被刪除。
第五:ClickHouse通過SummingMergeTree來支持對主鍵列進行預先聚合。在后臺 Compaction時,會將主鍵相同的多行進行sum求和,然后使用一行數據取而代之,從而大幅度降低存儲空間占用,提升聚合計算性能。
第六:AggregatingMergeTree也是預先聚合引擎的一種,用于提升聚合計算的性能。與SummingMergeTree的區別在于:SummingMergeTree對非主鍵列進行sum聚合,而 AggregatingMergeTree則可以指定各種聚合函數。
4.2 MergeTree的建表語法:
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAUErEMAMLERLALLIZED|ALIAS expr],
省略...
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, 省略...]
介紹一下其中的幾個關鍵選項:
- PARTITION BY:分區鍵。指定表數據以何種標準進行分區。分區鍵既可以是單個列字段,也可以通過元組的形式使用多個列字段,同時它也支持使用列表達式。
- ORDER BY:排序鍵,用于指定在一個數據片段內,數據以何種標準排序。默認情況下主鍵(PRIMARY KEY)與排序鍵相同。
- PRIMARY KEY:主鍵。聲明后會依照主鍵字段生成一級索引。默認情況下,主鍵與排序鍵(ORDER BY)相同,所以通常直接使用ORDER BY代為指定主鍵。
- SETTINGS:index_granularity選項表示索引的粒度,默認值為8192。MergeTree 索引在默認情況下,每間隔8192行數據才生成一條索引。
- SAMPLE BY:抽樣表達式,用于聲明數據以何種標準進行采樣。
注意settings中的重要參數:
- index_granularity 默認是8192
- index_granularity_bytes 默認10M,需要通過enable_mixed_granularity_parts=1來開啟
五:ClickHouse 工作原理
MergeTree表引擎的內部工作細節!最終就是告訴你:為什么clickhouse做查詢分析,那么快?
ClickHouse從OLAP場景需求出發,定制開發了一套全新的高效列式存儲引擎,并且實現了數據有序存儲、主鍵索引、稀疏索引、數據Sharding、數據Partitioning、TTL、主備復制等豐富功能。這些功能共同為ClickHouse極速的分析性能奠定了基礎。
5.1 數據分區
CREATE TABLE user(
id String,Name String,age UInt8,create_time DateTime
) ENGINE = MergeTree
PARTITION BY toYYYYMMDD(create_time)
ORDER BY age SETTINGS index_granularity = 8192
關于表分區目錄結構:MergeTree 表的分區目錄物理結構:
關于這些文件的解釋:
- 分區目錄:20210427_1_1_0,一個分區可能會有多個不同的目錄,該目錄下存儲該分區的數據及其他各種形式的數據。后臺會執行合并,把相同分區的多個目錄合并到一個分區。
- checksums.txt:校驗文件。使用二進制格式存儲。它保存了余下各類文件(primary.idx、count.txt等)的size大小及size的哈希值,用于快速校驗文件的完整性和正確性。
- columns.txt:列信息文件,使用明文格式存儲。用于保存此數據分區下的列字段信息。
- count.txt:計數文件,使用明文格式存儲。用于記錄當前數據分區目錄下數據的總行數。
- primary.idx:一級索引文件,主鍵索引文件。
- xxx.bin:數據文件,使用壓縮格式存儲,默認為LZ4壓縮格式,用于存儲某一列的數據,每一列都對應一個該文件,如列date為date.bin。
- xxx.mrk2:列字段標記文件,如果使用了自適應大小的索引間隔,則標記文件以.mrk2命名,否則以.mrk命名。它建立primary.idx稀疏索引與xxx.bin數據文件之間的映射關系,先通過主鍵索引找到數據的偏移量,然后去 xxx.bin數據文件中找到真實數據。
- ...還有二級索引和分區鍵相關信息文件等等。
相比于行式存儲,列式存儲在分析場景下有著許多優良的特性。
- 分析場景中往往需要讀大量行但是少數幾個列。在行存模式下,數據按行連續存儲,所有列的數據都存儲在一個block中,不參與計算的列在IO時也要全部讀出,讀取操作被嚴重放大。而列存模式下,只需要讀取參與計算的列即可,極大的減低了IO cost,加速了查詢。
- 同一列中的數據屬于同一類型,壓縮效果顯著。列存往往有著高達十倍甚至更高的壓縮比,節省了大量的存儲空間,降低了存儲成本。
- 更高的壓縮比意味著更小的data size,從磁盤中讀取相應數據耗時更短。
- 自由的壓縮算法選擇。不同列的數據具有不同的數據類型,適用的壓縮算法也就不盡相同。可以針對不同列類型,選擇最合適的壓縮算法。
- 高壓縮比,意味著同等大小的內存能夠存放更多數據,系統cache效果更好。
5.4 一級索引
關于一級索引:MergeTree的主鍵使用PRIMARY KEY定義,待主鍵定義之后,MergeTree會依據index_granularity 間隔(默認 8192 行),為數據表生成一級索引并保存至 primary.idx 文件內。
一級索引是稀疏索引,意思就是說:每一段數據生成一條索引記錄,而不是每一條數據都生成索引,如果是每一條數據都生成索引,則是稠密索引。稀疏索引的好處,就是少量的索引標記,就能記錄大量的數據區間位置信息,比如不到24414條標記信息,就能為2E條數據提供索引(算法:200000000/8192)。
在 ClickHouse中,一級索引常駐內存。總的來說:一級索引和標記文件一一對齊,兩個索引標記之間的數據,就是一個數據區間,在數據文件中,這個數據區間的所有數據,生成一個壓縮數據塊。需要注意的是:ClickHouse的主鍵索引與MySQL等數據庫不同,它并不用于去重,即便primary key相同的行,也可以同時存在于數據庫中。要想實現去重效果,需要結合具體的表引擎ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree實現。
5.5.二級索引
關于二級索引:又稱之為跳數索引。目的和一級索引一樣,是為了減少待搜尋的數據的范圍。
跳數索引的默認是關閉的,需要通過SET allow_experimental_data_skipping_indices = 1 來開啟,索引生成粒度由granularity控制,如果生成了二級索引,則會在分區目錄下生成額外的:skp_idx_[Column].idx與skp_idx_[Column].mrk文件。
跳數索引的生成規則:按照特定規則每隔granularity個index_granularity條數據,就會生成一條跳數索引。 比如minmax跳數索引,生成的是:granularity個index_granularity條數據內的最大值最小值生成一條索引,如果將來需要針對構建二級索引的這個字段求最大值最小值,則可以幫助提高效率。
跳數索引一共支持四種類型:minmax(最大最小)、set(去重集合)、ngrambf_v1(ngram分詞布隆索引)和tokenbf_v1(標點符號分詞布隆索引),一張數據表支持同時聲明多個跳數索引。比如:
GRANULARITY = 你在創建二級索引索引的指定的
INDEX_GRANULARITY = 8192 GRANULARITY * INDEX_GRANULARITY
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, O) GRANULARITY 5,
INDEX d ID TYPE tokenbf_v1(256, 2, 0) GRANULARITY 5
) ENGINE= MergeTree()
order by id;
關于跳數索引支持的多種類型的區別:
- minmax:以index_granularity為單位,存儲指定表達式計算后的min、max值;在等值和范圍查詢中能夠幫助快速跳過不滿足要求的塊,減少IO。
- set(max_rows):以index granularity為單位,存儲指定表達式的distinct value集合,用于快速判斷等值查詢是否命中該塊,減少IO。
- ngrambf_v1(n,size_of_bloom_filter_in_bytes,number_of_hash_functions, random_seed):將string進行ngram分詞后,構建bloom filter,能夠優化等值、like、in等查詢條件。
- tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):與ngrambf_v1類似,區別是不使用ngram進行分詞,而是通過標點符號進行詞語分割。
- bloom_filter([false_positive]):對指定列構建bloom filter,用于加速等值、like、in等查詢條件的執行。
5.6 數據壓縮
關于數據壓縮:ClickHouse的數據存儲文件column.bin中存儲是一列的數據,由于一列是相同類型的數據,所以方便高效壓縮。
在進行壓縮的時候,請注意:一個壓縮數據塊由頭信息和壓縮數據兩部分組成,頭信息固定使用9位字節表示,具體由1個UInt8(1字節)整型和 2個UInt32(4字節)整型組成,分別代表使用的壓縮算法類型、壓縮后的數據大小和壓縮前的數據大小。
單個壓縮數據塊的體積,按照其壓縮前的數據字節大小,都被嚴格控制在64KB~1MB,其上下限分別由min_compress_block_size(默認65536=64KB)與 max_compress_block_size(默認1048576=1M)參數指定。
具體壓縮規則:
原理的說法:每8192條記錄,其實就是一條一級索引一個索引區間 壓縮成一個數據塊。自適應壓縮
- 單個批次數據size < 64KB:如果單個批次數據小于64KB,則繼續獲取下一批數據,直至累積到size >= 64KB時,生成下一個壓縮數據塊。如果平均每條記錄小于8byte,多個數據批次壓縮成一個數據塊。
- 單個批次數據64KB<= size <=1MB:如果單個批次數據大小恰好在64KB 與1MB之間,則直接生成下一個壓縮數據塊。
- 單個批次數據size > 1MB:如果單個批次數據直接超過1MB,則首先按照1MB大小截斷并生成下一個壓縮數據塊。剩余數據繼續依照上述規則執行。此時,會出現一個批次數據生成多個壓縮數據塊的情況。如果平均每條記錄的大小超過 128byte,則會把當前這一個批次的數據壓縮成多個數據塊。
5.7 數據標記
關于數據標記:數據標記文件也與.bin文件一一對應。即每一個列字段[Column].bin文件都有一個與之對應的 [Column].mrk2 數據標記文件,用于記錄數據在 .bin 文件中的偏移量信息。
一行標記數據使用一個元組表示,元組內包含兩個整型數值的偏移量信息。它們分別表示在此段數據區間內,在對應的.bin壓縮文件中,壓縮數據塊的起始偏移量;以及將該數據壓縮塊解壓后,其未壓縮數據的起始偏移量。每一行標記數據都表示了一個片段的數據(默認8192行)在.bin壓縮文件中的讀取位置信息。標記數據與一級索引數據不同,它并不能常駐內存,而是使用LRU(最近最少使用)緩存策略加快其取用速度。
總結數據讀取流程:先根據一級索引,找到標記文件中的對應數據壓縮塊信息(壓縮塊在.bin文件中的起始偏移量和未壓縮之前該條數據的是偏移量)然后從.bin文件中,把壓縮塊加載到內存,解壓縮之后,執行讀取。建立了主鍵索引到數據文件的映射!
5.8 查詢數據
指定分區 ==> 指定字段(xxx.bin)==> 根據一級索引(primary.idx)定位到 標記文件(name.mrk2)中的那一條記錄 ==> 掃描對應字段的 mark 標記文件 獲取兩個偏移量信息(當前要查找的數據,處于這個.bin數據文件中的那個壓縮數據塊,這個壓縮數據塊在 .bin 文件的偏移量,這個壓縮數據塊解壓縮 出來之后,要找的數據在當前這個壓縮數據快的偏移量)==> 根據第一個偏移量去.bin文件中定位到一個壓縮數據快 ==> 讀取數據到內存執行解壓縮 ==> 根據第二個偏移量去內存解壓縮數據中找到對應的數據。
提高數據查詢效率的核心原則只有一個:誰做的輔助動作能快速的幫助我們去快速降低待搜尋的數據范圍。
六:總結
在大數據分析領域中,傳統的大數據分析需要不同框架和技術組合才能達到最終的效果,在人力成本,技術能力和硬件成本上以及維護成本讓大數據分析變得成為昂貴的事情。讓很多中小型企業非常苦惱,不得不被迫租賃第三方大型公司的數據分析服務。ClickHouse開源的出現讓許多想做大數據并且想做大數據分析的很多公司和企業耳目一新。ClickHouse 正是以不依賴Hadoop 生態、安裝和維護簡單、查詢速度快、可以支持SQL等特點在大數據分析領域越走越遠。
關注IT巔峰技術,私信作者,獲取以下2021全球架構師峰會PDF資料。