5000字6圖 | 一篇文章幫你理解大數(shù)據(jù)列式存儲(chǔ)(干貨收藏)

什么是列式存儲(chǔ)
所謂行式存儲(chǔ),指存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)時(shí),在底層的存儲(chǔ)介質(zhì)上,數(shù)據(jù)是以行的方式來(lái)組織的,即存儲(chǔ)完一條記錄的所有字段,再存儲(chǔ)下一條數(shù)據(jù)的所以字段,以此類推;
所謂列式存儲(chǔ),指存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)時(shí),在底層的存儲(chǔ)介質(zhì)上,數(shù)據(jù)是以列的方式來(lái)組織的,即存儲(chǔ)完若干條記錄的首個(gè)字段后,再存儲(chǔ)這些記錄的第二個(gè)字段,然后是這些記錄的第三個(gè)字段,以此類推,當(dāng)這些記錄的所有字段存儲(chǔ)完畢后,再按照這種方式,組織存儲(chǔ)下一批若干條記錄的所有字段;
比如需要存儲(chǔ)以下記錄:

              姓名    年齡    工資
              小張    18    10000
              小王    19    11000
              小李    20    12000
              小趙    21    13000
              小周    22    14000
              小吳    23    15000
              小鄭    24    16000
              小楊    25    17000
這行存儲(chǔ)格式,底層是這樣組織的:(示意圖)









而列存儲(chǔ)格式,底層是這樣組織的:(示意圖)






說(shuō)明:

以上只是示意圖,實(shí)際存儲(chǔ)時(shí),往往還會(huì)存儲(chǔ)一些元數(shù)據(jù),比如校驗(yàn)信息,統(tǒng)計(jì)信息等等;(列式存儲(chǔ)往往會(huì)存儲(chǔ)更多的元數(shù)據(jù)信息,以便于檢索/查詢/統(tǒng)計(jì)等)
行存儲(chǔ)與列存儲(chǔ),存儲(chǔ)的都是結(jié)構(gòu)化數(shù)據(jù);(非結(jié)構(gòu)化數(shù)據(jù),就無(wú)所謂行和列了)
行存儲(chǔ)和列存儲(chǔ),描述的是底層存儲(chǔ)介質(zhì)上,數(shù)據(jù)的組織形式,而存儲(chǔ)介質(zhì)可以是磁盤,也可以是內(nèi)存;存儲(chǔ)介質(zhì)的上次建筑,可以是文件系統(tǒng),也可以是對(duì)象存儲(chǔ);
很多數(shù)據(jù)庫(kù),在底層即支持行存儲(chǔ),也支持列存儲(chǔ),這兩者有時(shí)是同時(shí)混合使用的;(比如 oracle12c,就引入了對(duì) In-Memory Column Store 的支持;再比如 TiDB, 底層同時(shí)支持行存儲(chǔ)引擎 TiKV 和 列存儲(chǔ)引擎 TiFlash);
深入了解列式存儲(chǔ) - 以 Parquet 為例
說(shuō)到當(dāng)下最流行的開源列式存儲(chǔ)引擎,就非 apache parquet 莫屬了。

絕大多數(shù)計(jì)算引擎,比如 apache spark/presto/impala/flink, 都將 parquet 作為首選的列示存儲(chǔ)引擎;
orc 是 apache hive 社區(qū)推出的,另一款開源列示存儲(chǔ)引擎;
apache hive 在早期只支持 orc 列示存儲(chǔ)的基礎(chǔ)上,又?jǐn)U展了對(duì) parquet 的支持;
有鑒于此,我們有必要以 parquet 為例,來(lái)深入了解下列式存儲(chǔ)。

以下是 parquet 底層,數(shù)據(jù)的組織格式:



相關(guān)術(shù)語(yǔ)解釋如下:

Block (hdfs block): 指 hdfs 文件系統(tǒng)的 block,parquet 是在 hdfs 文件之上的數(shù)據(jù)組織格式;(當(dāng)然現(xiàn)在很多對(duì)象存儲(chǔ)系統(tǒng),比如 S3,也支持 parquet 存儲(chǔ)格式);
File: 指 hdfs 文件,使用 parquet 格式時(shí),每個(gè) hdfs 文件底層必須包括 parquet 元數(shù)據(jù) - 事實(shí)上該文件底層可以不包含數(shù)據(jù),但必須包含元數(shù)據(jù);(當(dāng)然現(xiàn)在很多對(duì)象存儲(chǔ)系統(tǒng),比如 S3,也支持 parquet 存儲(chǔ)格式);
Row group: 文件底層的所有數(shù)據(jù),在邏輯上被水平切割,每個(gè) row group 存儲(chǔ)的都是一份這些被水平切割后的數(shù)據(jù)。(row group 是對(duì)數(shù)據(jù)的水平切分比如上圖中,就顯示了 row group 0 和 row group1 兩個(gè) row group);
Column chunk: 每個(gè) row group 底層都包含一系列 column chunk,每個(gè)colum 都有一個(gè)對(duì)應(yīng)的 column chunk;(數(shù)據(jù)有多少列,水平切割的每個(gè) row group底層,就有多少個(gè) column chunk);






Page: Column chunks 被進(jìn)一步切分為若干個(gè) page 頁(yè),page 是壓縮和編碼的最小單位;(比如上如,row group0 的 Column a,就顯示了兩個(gè) page: page a 和 page b);
概括如下:

每個(gè)文件由一個(gè)或多個(gè) row group 構(gòu)成,每個(gè) row group 包含了多個(gè) column chunk (column chunk 和 column 是 一一對(duì)應(yīng)的),Column chunks 包含了一個(gè)或多個(gè) page;
MR 等計(jì)算作業(yè),其并行操作的單位是 File/Row Group;
IO 的單位是 Column chunk;
編碼和壓縮的單位是 Page;
行式存儲(chǔ)與列式存儲(chǔ)的優(yōu)劣勢(shì)都有哪些
通過上述對(duì)底層數(shù)據(jù)組織格式的對(duì)比,不難發(fā)現(xiàn),行存儲(chǔ)有以下特點(diǎn):

行存儲(chǔ)將每條數(shù)據(jù)的所有列連續(xù)存儲(chǔ)在一起,一條記錄接著一條記錄;
行存儲(chǔ)中數(shù)據(jù)寫入的成本較低,適合數(shù)據(jù)有頻繁更新的場(chǎng)景;
通過使用索引,能大幅提高行存儲(chǔ)的數(shù)據(jù)查詢速度;
行存儲(chǔ)是傳統(tǒng)的數(shù)據(jù)組織形式,更適合傳統(tǒng)的 OLTP 系統(tǒng);(OLTP 數(shù)據(jù)庫(kù)表的設(shè)計(jì)強(qiáng)調(diào)范式,底層一般有多張有關(guān)聯(lián)關(guān)系的窄表)
而列存儲(chǔ)有以下特點(diǎn):

列存儲(chǔ)將多行記錄的列連續(xù)存儲(chǔ)在一起,一列接著一列;
由于連續(xù)存儲(chǔ)在一起的列的數(shù)據(jù)類型都一樣,所以數(shù)據(jù)壓縮率更高,更省存儲(chǔ)空間;
列存儲(chǔ)中數(shù)據(jù)查詢的成本較低,特別適合分析時(shí)只查詢部分列的場(chǎng)景,因?yàn)椴恍枰獟呙?讀取不需要查詢的列;
列存儲(chǔ)由于數(shù)據(jù)更新成本較高,一般適合讀多寫少的場(chǎng)景;(但是不代表不能更新?。?br>列存儲(chǔ)是新型數(shù)據(jù)組織形式,更適合 OLAP 分析型系統(tǒng);(OLAP數(shù)據(jù)庫(kù)表的設(shè)計(jì)強(qiáng)調(diào)反范式,底層一般是星型模式的若干張事實(shí)表和維度表,傾向使用大寬表)
列式存儲(chǔ)與數(shù)據(jù)質(zhì)量
各廠各司在其數(shù)據(jù)倉(cāng)庫(kù)或數(shù)據(jù)湖泊的數(shù)據(jù)規(guī)范部分,大都強(qiáng)調(diào)要盡可能地使用列式存儲(chǔ)格式,而盡可能不要使用行式存儲(chǔ)格式。

究其原因,一方面是列式存儲(chǔ)相比行式存儲(chǔ),其壓縮率更高讀寫效率更快;另一方面是因?yàn)?,相比行式存?chǔ),列式存儲(chǔ)的數(shù)據(jù)質(zhì)量更高!

之所以說(shuō)列式存儲(chǔ)能提高數(shù)據(jù)質(zhì)量,更準(zhǔn)確地說(shuō),是有些行式存儲(chǔ)會(huì)降低數(shù)據(jù)質(zhì)量,比如 csv/tsv 等 TextFile。

比如,以我們熟悉的 HIVE 為例,一個(gè)常見的 DDL 語(yǔ)句如下:

CREATE TABLE student_text_test(                       
   id double,                                     
   name string,                                   
   sex double,                                    
   age double,                                    
   class double,                                  
   address string)    
   ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
 STORED AS TEXTFILE;
大家可以看到,TextFile 的 DDL 語(yǔ)句中,需要通過 xx terminated by xx 來(lái)指定列分隔符和行分隔符(列分隔符默認(rèn)是'\001',行分隔符默認(rèn)是'\n');
而上游rdbms表中某些字段,可能會(huì)包含特殊字符比如換行符,此時(shí)就可能會(huì)跟上述textfile 表的 DDL 語(yǔ)句中指定的列分隔符或分隔符沖突;
后續(xù)通過數(shù)據(jù)同步工具比如 sqoop/datax/spark sql 等同步上游數(shù)據(jù)到 hive中 textfile 格式的表時(shí),同步作業(yè)可以執(zhí)行成功;
但是后續(xù)再查詢?cè)撏降?textfile 格式的表時(shí),因?yàn)榇髷?shù)據(jù)schema on read 的特性,在讀數(shù)據(jù)時(shí)按就會(huì)按照 DDL 中指定的行和列分隔符來(lái)切割數(shù)據(jù),此時(shí)就可能會(huì)因?yàn)樾蟹指舴斐蓴?shù)據(jù)切割混亂,從而查出大量 NULL 字段的錯(cuò)亂數(shù)據(jù);
不幸的是,上游 RDBMS 中的數(shù)據(jù)往往不受大數(shù)據(jù)部門管控,會(huì)出現(xiàn)各種各樣的奇怪?jǐn)?shù)據(jù);
更不幸的是,目前hive的textfile只支持\n作為行分隔符(驗(yàn)證了cdh6.3,也就是hive2.1)
所以,正是出于數(shù)據(jù)質(zhì)量的原因,各廠各司在其數(shù)據(jù)倉(cāng)庫(kù)或數(shù)據(jù)湖泊的數(shù)據(jù)規(guī)范部分,大都強(qiáng)調(diào):

要盡可能地使用列式存儲(chǔ)格式比如orc/parquet,而盡可能不要使用行式存儲(chǔ)格式textfile;
數(shù)倉(cāng)中各層,包括 ods/dwd/dws/ads ,以及業(yè)務(wù)臨時(shí)表與技術(shù)臨時(shí)表,都盡量不要使用textfile;
除非是選用的數(shù)據(jù)同步工具不支持orc/parquet(比如sqoop),且此時(shí)也僅僅只能在 ods層的落地表中使用textfile, 后面 dws等各層需要切換使用列示存儲(chǔ) orc/parquet;
必要時(shí),對(duì)數(shù)據(jù)同步工具重新進(jìn)行技術(shù)選型,比如拋棄不支持列式存儲(chǔ)的 sqoop, 而選用支持列式存儲(chǔ)的 spark sql/datax 等。

作者:教你學(xué)懂大數(shù)據(jù)


歡迎關(guān)注微信公眾號(hào) :教你學(xué)懂大數(shù)據(jù)