5000字6圖 | 一篇文章幫你理解大數(shù)據(jù)列式存儲(干貨收藏)
什么是列式存儲
所謂行式存儲,指存儲結(jié)構(gòu)化數(shù)據(jù)時(shí),在底層的存儲介質(zhì)上,數(shù)據(jù)是以行的方式來組織的,即存儲完一條記錄的所有字段,再存儲下一條數(shù)據(jù)的所以字段,以此類推;
所謂列式存儲,指存儲結(jié)構(gòu)化數(shù)據(jù)時(shí),在底層的存儲介質(zhì)上,數(shù)據(jù)是以列的方式來組織的,即存儲完若干條記錄的首個(gè)字段后,再存儲這些記錄的第二個(gè)字段,然后是這些記錄的第三個(gè)字段,以此類推,當(dāng)這些記錄的所有字段存儲完畢后,再按照這種方式,組織存儲下一批若干條記錄的所有字段;
比如需要存儲以下記錄:
姓名 年齡 工資
小張 18 10000
小王 19 11000
小李 20 12000
小趙 21 13000
小周 22 14000
小吳 23 15000
小鄭 24 16000
小楊 25 17000
這行存儲格式,底層是這樣組織的:(示意圖)
而列存儲格式,底層是這樣組織的:(示意圖)
說明:
以上只是示意圖,實(shí)際存儲時(shí),往往還會存儲一些元數(shù)據(jù),比如校驗(yàn)信息,統(tǒng)計(jì)信息等等;(列式存儲往往會存儲更多的元數(shù)據(jù)信息,以便于檢索/查詢/統(tǒng)計(jì)等)
行存儲與列存儲,存儲的都是結(jié)構(gòu)化數(shù)據(jù);(非結(jié)構(gòu)化數(shù)據(jù),就無所謂行和列了)
行存儲和列存儲,描述的是底層存儲介質(zhì)上,數(shù)據(jù)的組織形式,而存儲介質(zhì)可以是磁盤,也可以是內(nèi)存;存儲介質(zhì)的上次建筑,可以是文件系統(tǒng),也可以是對象存儲;
很多數(shù)據(jù)庫,在底層即支持行存儲,也支持列存儲,這兩者有時(shí)是同時(shí)混合使用的;(比如 oracle12c,就引入了對 In-Memory Column Store 的支持;再比如 TiDB, 底層同時(shí)支持行存儲引擎 TiKV 和 列存儲引擎 TiFlash);
深入了解列式存儲 - 以 Parquet 為例
說到當(dāng)下最流行的開源列式存儲引擎,就非 apache parquet 莫屬了。
絕大多數(shù)計(jì)算引擎,比如 apache spark/presto/impala/flink, 都將 parquet 作為首選的列示存儲引擎;
orc 是 apache hive 社區(qū)推出的,另一款開源列示存儲引擎;
apache hive 在早期只支持 orc 列示存儲的基礎(chǔ)上,又?jǐn)U展了對 parquet 的支持;
有鑒于此,我們有必要以 parquet 為例,來深入了解下列式存儲。
以下是 parquet 底層,數(shù)據(jù)的組織格式:
相關(guān)術(shù)語解釋如下:
Block (hdfs block): 指 hdfs 文件系統(tǒng)的 block,parquet 是在 hdfs 文件之上的數(shù)據(jù)組織格式;(當(dāng)然現(xiàn)在很多對象存儲系統(tǒng),比如 S3,也支持 parquet 存儲格式);
File: 指 hdfs 文件,使用 parquet 格式時(shí),每個(gè) hdfs 文件底層必須包括 parquet 元數(shù)據(jù) - 事實(shí)上該文件底層可以不包含數(shù)據(jù),但必須包含元數(shù)據(jù);(當(dāng)然現(xiàn)在很多對象存儲系統(tǒng),比如 S3,也支持 parquet 存儲格式);
Row group: 文件底層的所有數(shù)據(jù),在邏輯上被水平切割,每個(gè) row group 存儲的都是一份這些被水平切割后的數(shù)據(jù)。(row group 是對數(shù)據(jù)的水平切分比如上圖中,就顯示了 row group 0 和 row group1 兩個(gè) row group);
Column chunk: 每個(gè) row group 底層都包含一系列 column chunk,每個(gè)colum 都有一個(gè)對應(yīng)的 column chunk;(數(shù)據(jù)有多少列,水平切割的每個(gè) row group底層,就有多少個(gè) column chunk);
Page: Column chunks 被進(jìn)一步切分為若干個(gè) page 頁,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 是 一一對應(yīng)的),Column chunks 包含了一個(gè)或多個(gè) page;
MR 等計(jì)算作業(yè),其并行操作的單位是 File/Row Group;
IO 的單位是 Column chunk;
編碼和壓縮的單位是 Page;
行式存儲與列式存儲的優(yōu)劣勢都有哪些
通過上述對底層數(shù)據(jù)組織格式的對比,不難發(fā)現(xiàn),行存儲有以下特點(diǎn):
行存儲將每條數(shù)據(jù)的所有列連續(xù)存儲在一起,一條記錄接著一條記錄;
行存儲中數(shù)據(jù)寫入的成本較低,適合數(shù)據(jù)有頻繁更新的場景;
通過使用索引,能大幅提高行存儲的數(shù)據(jù)查詢速度;
行存儲是傳統(tǒng)的數(shù)據(jù)組織形式,更適合傳統(tǒng)的 OLTP 系統(tǒng);(OLTP 數(shù)據(jù)庫表的設(shè)計(jì)強(qiáng)調(diào)范式,底層一般有多張有關(guān)聯(lián)關(guān)系的窄表)
而列存儲有以下特點(diǎn):
列存儲將多行記錄的列連續(xù)存儲在一起,一列接著一列;
由于連續(xù)存儲在一起的列的數(shù)據(jù)類型都一樣,所以數(shù)據(jù)壓縮率更高,更省存儲空間;
列存儲中數(shù)據(jù)查詢的成本較低,特別適合分析時(shí)只查詢部分列的場景,因?yàn)椴恍枰獟呙?讀取不需要查詢的列;
列存儲由于數(shù)據(jù)更新成本較高,一般適合讀多寫少的場景;(但是不代表不能更新?。?br>列存儲是新型數(shù)據(jù)組織形式,更適合 OLAP 分析型系統(tǒng);(OLAP數(shù)據(jù)庫表的設(shè)計(jì)強(qiáng)調(diào)反范式,底層一般是星型模式的若干張事實(shí)表和維度表,傾向使用大寬表)
列式存儲與數(shù)據(jù)質(zhì)量
各廠各司在其數(shù)據(jù)倉庫或數(shù)據(jù)湖泊的數(shù)據(jù)規(guī)范部分,大都強(qiáng)調(diào)要盡可能地使用列式存儲格式,而盡可能不要使用行式存儲格式。
究其原因,一方面是列式存儲相比行式存儲,其壓縮率更高讀寫效率更快;另一方面是因?yàn)?,相比行式存儲,列式存儲的?shù)據(jù)質(zhì)量更高!
之所以說列式存儲能提高數(shù)據(jù)質(zhì)量,更準(zhǔn)確地說,是有些行式存儲會降低數(shù)據(jù)質(zhì)量,比如 csv/tsv 等 TextFile。
比如,以我們熟悉的 HIVE 為例,一個(gè)常見的 DDL 語句如下:
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 語句中,需要通過 xx terminated by xx 來指定列分隔符和行分隔符(列分隔符默認(rèn)是'\001',行分隔符默認(rèn)是'\n');
而上游rdbms表中某些字段,可能會包含特殊字符比如換行符,此時(shí)就可能會跟上述textfile 表的 DDL 語句中指定的列分隔符或分隔符沖突;
后續(xù)通過數(shù)據(jù)同步工具比如 sqoop/datax/spark sql 等同步上游數(shù)據(jù)到 hive中 textfile 格式的表時(shí),同步作業(yè)可以執(zhí)行成功;
但是后續(xù)再查詢該同步的 textfile 格式的表時(shí),因?yàn)榇髷?shù)據(jù)schema on read 的特性,在讀數(shù)據(jù)時(shí)按就會按照 DDL 中指定的行和列分隔符來切割數(shù)據(jù),此時(shí)就可能會因?yàn)樾蟹指舴斐蓴?shù)據(jù)切割混亂,從而查出大量 NULL 字段的錯(cuò)亂數(shù)據(jù);
不幸的是,上游 RDBMS 中的數(shù)據(jù)往往不受大數(shù)據(jù)部門管控,會出現(xiàn)各種各樣的奇怪?jǐn)?shù)據(jù);
更不幸的是,目前hive的textfile只支持\n作為行分隔符(驗(yàn)證了cdh6.3,也就是hive2.1)
所以,正是出于數(shù)據(jù)質(zhì)量的原因,各廠各司在其數(shù)據(jù)倉庫或數(shù)據(jù)湖泊的數(shù)據(jù)規(guī)范部分,大都強(qiáng)調(diào):
要盡可能地使用列式存儲格式比如orc/parquet,而盡可能不要使用行式存儲格式textfile;
數(shù)倉中各層,包括 ods/dwd/dws/ads ,以及業(yè)務(wù)臨時(shí)表與技術(shù)臨時(shí)表,都盡量不要使用textfile;
除非是選用的數(shù)據(jù)同步工具不支持orc/parquet(比如sqoop),且此時(shí)也僅僅只能在 ods層的落地表中使用textfile, 后面 dws等各層需要切換使用列示存儲 orc/parquet;
必要時(shí),對數(shù)據(jù)同步工具重新進(jìn)行技術(shù)選型,比如拋棄不支持列式存儲的 sqoop, 而選用支持列式存儲的 spark sql/datax 等。
作者:教你學(xué)懂大數(shù)據(jù)
歡迎關(guān)注微信公眾號 :教你學(xué)懂大數(shù)據(jù)