数据湖常用查询优化技术
經(jīng)濟(jì)發(fā)展有周期,人的思想活動(dòng)也是有周期的,是時(shí)候進(jìn)行一場(chǎng)文化領(lǐng)域的整風(fēng)運(yùn)動(dòng)了,尤其是那些空談?wù)`國(guó),亂教誤人子弟的,就是缺少了對(duì)其思想改造的過(guò)程,嚴(yán)重脫離群眾,是時(shí)候要常態(tài)化地下放了。
本文首發(fā)微信公眾號(hào):碼上觀世界
1
MinMax
開(kāi)放式數(shù)據(jù)格式文件的的元數(shù)據(jù)信息部分通常都包含當(dāng)前文件每個(gè)列的最大、最小值,比如下圖中的parquet文件包含兩個(gè)字段:year和uid,并且
file1.parquet中列year的最大和最小值分別是2019和2018,列uid的最大和最小值分別是23000和12000,file2.parquet中列year的最大和最小值分別是2020和2018,列uid的最大和最小值分別是14000和12000,file3.parquet中列year的最大和最小值分別是2020和2020,列uid的最大和最小值分別是25000和23000:
當(dāng)我們進(jìn)行查詢(xún)
select * from event where year=2019 and uid=20000因?yàn)檫@些元數(shù)據(jù)信息在數(shù)據(jù)寫(xiě)入文件時(shí)最終收集,因此在查詢(xún)時(shí)候,很容易利用這些統(tǒng)計(jì)信息過(guò)濾掉不符合條件的數(shù)據(jù)文件。示例中,根據(jù)year=2020和uid=20000查詢(xún)到符合條件的數(shù)據(jù)文件為file1.parquet,另外兩個(gè)數(shù)據(jù)文件直接過(guò)濾掉了,減少了不必須的文件讀取。
為了獲取更好的過(guò)濾效果,MinMax通常進(jìn)行全局排序,但是適合排序字段較少的情況,比如1個(gè)字段,當(dāng)排序字段多于1個(gè),依據(jù)索引的最左匹配規(guī)則,只有查詢(xún)字段覆蓋所有所有索引字段才能獲得最好的查詢(xún)效果,否則過(guò)濾效果將大打折扣,為此需要更合適的索引,比如Z-Order。
2
Z-Order
因?yàn)镸inMax索引包括多個(gè)字段時(shí),不能保證數(shù)據(jù)的聚集性,而利用Z-Order索引能夠獲得比MinMax平均更好的數(shù)據(jù)聚集性。Z-Order原理是把天然沒(méi)有有序性的多維數(shù)據(jù)以某種方式映射成一維數(shù)據(jù)進(jìn)行比較。映射后的一維數(shù)據(jù),能夠保證各個(gè)原始維度按照同種程度去保證其聚集性。如下圖所示:
對(duì)X,Y這兩個(gè)維度進(jìn)行比特位的交叉組值,形成了Interleave Index進(jìn)而得出一個(gè)新的值,這個(gè)值被稱(chēng)作Z-Value。從圖中,可以看到針對(duì)X,Y這兩個(gè)字段的數(shù)據(jù),生成的z-value會(huì)呈現(xiàn)出一個(gè)Z形嵌套。按照這樣的一個(gè)結(jié)構(gòu),在按照Z(yǔ)-Value排序時(shí),能夠同時(shí)保證X,Y兩個(gè)字段的聚集性。
實(shí)現(xiàn)Z-Order 的一個(gè)前提是需要保證數(shù)據(jù)以保序的方式映射成一個(gè)正整型,但參與排序的字段類(lèi)型很多,如String、Long、DateTime、Double等,如何將這些不同類(lèi)型的值映射為正整型就是個(gè)問(wèn)題。實(shí)踐中,雖然可以將String類(lèi)型取固定的前幾位字符轉(zhuǎn)為二進(jìn)制來(lái)進(jìn)行映射,但也帶來(lái)了信息損失。另外,即使是正整型數(shù)據(jù),由于其數(shù)據(jù)分布不同,可能導(dǎo)致映射的結(jié)果不符合Z-Order曲線的嵌套分布。比如,X的取值是0,1,2,3,4,5,6,7,Y的取值是8,16,24,32這種,計(jì)算出來(lái)的z-value排序效果實(shí)際上和數(shù)據(jù)按照order by y,x的效果是一樣的。也就是說(shuō)這種排序并沒(méi)有帶來(lái)額外的好處,對(duì)于X的聚集性無(wú)法保證。
為了獲取更好的過(guò)濾效果,Z-Order也需要進(jìn)行全局排序,但是Z-Order排序字段越多,排序效果也會(huì)越差。建議2-4個(gè)。
3
Bloom Flilter
Bloom Flilter是一種空間節(jié)約型的概率數(shù)據(jù)結(jié)構(gòu),通過(guò)一個(gè)長(zhǎng)度為M的位數(shù)組來(lái)存儲(chǔ)元素,可以添加元素但不能刪除元素。每個(gè)元素使用k個(gè)哈希函數(shù)生成k個(gè)數(shù)值,大小位于區(qū)間[0,數(shù)組長(zhǎng)度-1]中,添加元素到Bloom Flilter時(shí),將相應(yīng)位置置為1。當(dāng)查詢(xún)是否存在相應(yīng)元素時(shí),只需要判斷k個(gè)位置的值是否全為1,如果不全是1,說(shuō)明不存在該元素,如果全是1,則不一定說(shuō)明存在該元素。k是一個(gè)遠(yuǎn)小于m的常數(shù),m是跟添加到Bloom Flilter的元素個(gè)數(shù)成正比。兩者的具體取值由Bloom Flilter的誤判(false positive)比例決定。示例見(jiàn)下圖:
圖中,Bloom Flilter長(zhǎng)度m為11,哈希函數(shù)個(gè)數(shù)k為2,添加兩個(gè)元素A和B,現(xiàn)在查詢(xún)?cè)谹、C、D,因?yàn)樵谹哈希映射的兩個(gè)位置都為1,且的確是A的哈希映射結(jié)果,所以能檢索到A存在。元素C因?yàn)椴粷M(mǎn)足所有哈希位置都為1,所以可以斷定C不存在(True Negative)。但是因?yàn)镈的哈希映射位置并非是D的哈希映射結(jié)果,即使其對(duì)應(yīng)的哈希位置都為1,也不能斷定D的存在,這對(duì)D來(lái)講,就是誤判(false positive)。
Bloom Filter利用少量哈希位來(lái)存儲(chǔ)和定位元素,無(wú)論存儲(chǔ)還是查詢(xún),其時(shí)間復(fù)雜度都是常量級(jí):O(k),只跟哈希函數(shù)的個(gè)數(shù)有關(guān)。其代價(jià)是有一定的沖突概率,數(shù)組長(zhǎng)度同添加的元素?cái)?shù)量成正比,當(dāng)數(shù)組長(zhǎng)度越長(zhǎng),哈希函數(shù)越多,沖突概率越小,反之,沖突概率越高。因此,在使用中,需要確定數(shù)組長(zhǎng)度和沖突概率。Bloom Flilter使用內(nèi)存維護(hù),且不存儲(chǔ)元素本身,相比其他數(shù)據(jù)結(jié)構(gòu),能獲得較高的空間和查詢(xún)效率。缺點(diǎn)是只能確定元素是否存在,不能確定元素的具體位置,且不支持范圍查詢(xún)和刪除操作。
4
bitmap indices
位圖索引(bitmap indices)是一種專(zhuān)為多個(gè)鍵的簡(jiǎn)單查詢(xún)而設(shè)計(jì)的。bitmap索引將每個(gè)被索引的列的值作為KEY,使用每個(gè)BIT表示一行,當(dāng)這行中包含這個(gè)值時(shí),設(shè)置為1,否則設(shè)置為0。應(yīng)用位圖索引的前提是記錄必須被按順序編號(hào),一般從0開(kāi)始。給出編號(hào)n,必須能夠很容易的找到對(duì)應(yīng)的記錄,如果記錄被存放在連續(xù)的塊,可以將編號(hào)n轉(zhuǎn)換成塊編號(hào)+塊內(nèi)偏移的表示以快速定位記錄位置。
位圖索引用一個(gè)位來(lái)對(duì)應(yīng)一條記錄,這便是記錄需要被編號(hào)的原因。instructor_info表如上圖,性別的值有男、女兩種,收入等級(jí)則劃分為5級(jí),既有5種值。在給性別屬性建立位圖索引時(shí),就會(huì)分別為male和female建立,對(duì)于male位圖來(lái)說(shuō),如果一條記錄的性別為male,則位圖上對(duì)應(yīng)的位會(huì)置1,female、收入等級(jí)位圖也采用相同的做法。
位圖索引的優(yōu)勢(shì)體現(xiàn)在根據(jù)多個(gè)鍵的查詢(xún)的時(shí)候,比如查詢(xún):
where gender=’f’ and income_level='L2'只需將gender=’f’的位圖索引和income_level='L2'的位圖索引取位與運(yùn)算即可:
除此之外,范圍查詢(xún)也是進(jìn)行數(shù)據(jù)統(tǒng)計(jì)時(shí)候常見(jiàn)操作,基于位圖索引的位或運(yùn)算很容易實(shí)現(xiàn)范圍查詢(xún),比如下面的查詢(xún):
where gender=’f’ and income_level>=’L2’我們只需要將income_level='L1'和income_level='L2'的位圖索引位或運(yùn)算,然后再跟gender=’f’的位圖索引位與運(yùn)算即可:
從上面示例中可見(jiàn),bitmap索引就是用位圖表示的索引,對(duì)列的每個(gè)鍵值建立一個(gè)位圖。所以相對(duì)于b-tree索引,占用的存儲(chǔ)空間非常小,創(chuàng)建和使用非常快。相比BloomFilter索引,bitmap索引不僅支持等值過(guò)濾,還支持范圍過(guò)濾。經(jīng)過(guò)良好編碼的位圖索引,還能夠獲得比BloomFilter索引更少的存儲(chǔ)空間和更精準(zhǔn)的匹配。但bitmap索引使用也有限制,比如適合建在值重復(fù)度高的列上,建議在100到100,000之間,如:職業(yè)、地市等。重復(fù)度過(guò)高則對(duì)比其他類(lèi)型索引沒(méi)有明顯優(yōu)勢(shì);重復(fù)度過(guò)低,則空間效率和性能會(huì)大大降低。對(duì)于經(jīng)常更新的列,也不適合使用bitmap索引。
5
小文件合并與去重
流式數(shù)據(jù)入湖伴隨著大量小文件的產(chǎn)生,根據(jù)文件產(chǎn)生的更新方式分為可追加的方式和非追加的方式兩種:
可追加的方式:以只讀事件日志的方式寫(xiě)入,如IoT事件
非追加的方式:以可更新的方式寫(xiě)入,如CDC事件
在高頻的流處理場(chǎng)景,每天都可能產(chǎn)生成百上千的新文件,基于Flink實(shí)時(shí)計(jì)算引擎,事務(wù)提交的間隔越短,產(chǎn)生的文件大小越小,數(shù)量越多。在有些數(shù)據(jù)湖系統(tǒng)的實(shí)現(xiàn)中,即使沒(méi)有可提交的數(shù)據(jù),也可能會(huì)生成空文件(但存在文件元數(shù)據(jù))。
這些文件隨后被寫(xiě)入對(duì)象存儲(chǔ)系統(tǒng),如AWS S3、阿里云OSS等,然后再通過(guò)查詢(xún)引擎,如Athena 、Trino等查詢(xún)數(shù)據(jù)。大量的小文件將嚴(yán)重拖慢系統(tǒng)響應(yīng)速度,因?yàn)樽x取每個(gè)文件,系統(tǒng)都要完成由下面三個(gè)基本步驟組成的動(dòng)作:
打開(kāi)文件
查找元數(shù)據(jù)
關(guān)閉文件
比如對(duì)ceberg來(lái)說(shuō),讀取一個(gè)文件的數(shù)據(jù),首先要打開(kāi)快照文件,從快照中獲取Manifenst文件,然后從Manifenst獲取數(shù)據(jù)文件,最后從數(shù)據(jù)文件中讀取數(shù)據(jù)。雖然打開(kāi)一個(gè)文件可能只需要數(shù)毫秒的時(shí)間,但是當(dāng)文件數(shù)量規(guī)模足夠大,這個(gè)時(shí)間開(kāi)銷(xiāo)就會(huì)達(dá)到無(wú)法忍受的程度。當(dāng)使用云上對(duì)象存儲(chǔ)服務(wù)時(shí),考慮到訪問(wèn)頻次限制和調(diào)用預(yù)算,讀取大量的文件有是無(wú)法接受的。
對(duì)于非追加的方式,有兩種處理方式:
COW(Copy-on-Write):每次更新事件,會(huì)以該事件所在文件為副本,創(chuàng)建新的文件,最新的數(shù)據(jù)由當(dāng)前最新的副本文件數(shù)據(jù)組成。COW會(huì)導(dǎo)致”寫(xiě)放大“和并發(fā)提交沖突問(wèn)題,適合于寫(xiě)少讀多的場(chǎng)景。
MOR(Merge-on-Read):每次創(chuàng)建增量更新的文件,最新的數(shù)據(jù)是由前一次提交的快照數(shù)據(jù)和當(dāng)前增量更新數(shù)據(jù)組成。MOR會(huì)導(dǎo)致查詢(xún)效率低,適合于寫(xiě)多讀少的場(chǎng)景。MOR通常實(shí)現(xiàn)為一個(gè)持續(xù)性地合并并提交增量更新的后臺(tái)進(jìn)程。
當(dāng)前三大數(shù)據(jù)湖技術(shù)中,Delta Lake 和 Iceberg僅支持 Copy-on-Write,因此它們不適合寫(xiě)負(fù)載重的場(chǎng)合,而Hudi同時(shí)支持Copy-on-Write和Merge-on-Read。Iceberg實(shí)現(xiàn)了 一種 Copy-on-Write 變體:每個(gè)快照只存儲(chǔ)增量數(shù)據(jù),最新全量數(shù)據(jù)由最新的快照通過(guò)引用的Manifest包含的所有數(shù)據(jù)文件組成。為了盡可能保證寫(xiě)入的效率,Iceberg將非追加的事件轉(zhuǎn)換為可追加的Insert事件和Delete事件,分別存儲(chǔ)在普通數(shù)據(jù)文件(data file)和刪除數(shù)據(jù)文件(delete file)這兩種類(lèi)型的文件中。而刪除數(shù)據(jù)文件存儲(chǔ)的內(nèi)容根據(jù)刪除方式分為文件路徑(position-delete)刪除和等值刪除(equality-delete)兩種,前者為解決在當(dāng)前快照周期內(nèi)反復(fù)增加和刪除相同主鍵記錄的問(wèn)題而引入,后者因?yàn)镮ceberg缺少主鍵索引,為了避免更新記錄去查詢(xún)歷史數(shù)據(jù)帶來(lái)的開(kāi)銷(xiāo),直接在刪除文件中記錄等值刪除,它適用于跨快照周期數(shù)據(jù)更新和刪除的場(chǎng)景。數(shù)據(jù)更新或刪除的方式雖然保證了寫(xiě)入的高吞吐,但也帶來(lái)了新的問(wèn)題:
每條更新事件都會(huì)涉及到兩個(gè)文件(追加數(shù)據(jù)文件和刪除數(shù)據(jù)文件),相比可追加存儲(chǔ)的方式,多了一份數(shù)據(jù)文件
在查詢(xún)時(shí)需要將追加數(shù)據(jù)文件與刪除數(shù)據(jù)文件關(guān)聯(lián),排除掉刪除數(shù)據(jù)記錄,特別是等值刪除方式因?yàn)闆](méi)有記錄事件在追加數(shù)據(jù)文件中的位置,需要遍歷所有的追加數(shù)據(jù)文件,在沒(méi)有數(shù)據(jù)索引的情況下,該過(guò)程會(huì)很漫長(zhǎng)
解決浙這些問(wèn)題的方式就是合并:將小文件合并成大文件,在合并小文件的過(guò)程中過(guò)濾掉刪除的數(shù)據(jù)記錄,從而提升查詢(xún)效率。這是目前最有效的方式,通文件合并讓計(jì)算引擎花費(fèi)更多時(shí)間在讀取數(shù)據(jù)內(nèi)容,而不是將時(shí)間花在頻繁地打開(kāi)文件、查找文件和關(guān)閉文件上面。
實(shí)現(xiàn)文件合并常見(jiàn)的方式是定時(shí)啟動(dòng)Spark 或者 Hadoop離線作業(yè)合并小文件,該過(guò)程通常較長(zhǎng),具體時(shí)長(zhǎng)視合并文件的大小而定。企業(yè)應(yīng)用通常啟動(dòng)獨(dú)立的集群運(yùn)行,比如基于自建集群服務(wù)或者EMR云服務(wù)。在實(shí)踐中,合并小文件會(huì)出現(xiàn)很多問(wèn)題,比較突出的是合并效率低和內(nèi)存溢出。為什么會(huì)有這種問(wèn)題呢?我們通過(guò)流程圖來(lái)看數(shù)據(jù)合并的過(guò)程:
如圖所示,合并數(shù)據(jù)文件時(shí)候,首先獲取待合并的數(shù)據(jù)文件列表,然后迭代讀取每個(gè)數(shù)據(jù)文件并將相關(guān)聯(lián)的刪除數(shù)據(jù)文件的數(shù)據(jù)加載到內(nèi)存中的刪除數(shù)據(jù)集合(delete set)。迭代讀取數(shù)據(jù)文件的過(guò)程,類(lèi)似數(shù)據(jù)庫(kù)中一個(gè)大表和一個(gè)小表進(jìn)行join的過(guò)程,大表是流式表,小表是構(gòu)建表,遍歷數(shù)據(jù)文件的每一條數(shù)據(jù)時(shí),在刪除文件數(shù)據(jù)集合中檢查是否存在,如果存在,當(dāng)前數(shù)據(jù)記錄就不會(huì)被保留。要知道每個(gè)數(shù)據(jù)文件可能關(guān)聯(lián)多個(gè)刪除數(shù)據(jù)文件,這些文件都是壓縮存儲(chǔ)的,比如parquet或avro,一旦刪除數(shù)據(jù)集加載到內(nèi)存,只有當(dāng)數(shù)據(jù)文件迭代結(jié)束之后才會(huì)釋放,因此很容易導(dǎo)致內(nèi)存溢出。
另外,當(dāng)前的Iceberg實(shí)現(xiàn)只合并了普通數(shù)據(jù)文件,對(duì)刪除數(shù)據(jù)文件并沒(méi)有合并,在更新數(shù)據(jù)頻繁的情況下,刪除數(shù)據(jù)文件數(shù)量也很可觀,不得不對(duì)其合并,這個(gè)也是在實(shí)踐中不得不考慮的問(wèn)題。
這里介紹了在合并文件過(guò)程常見(jiàn)的問(wèn)題,實(shí)際上還有很多細(xì)節(jié)問(wèn)題需要考慮,這里簡(jiǎn)單匯總下,做個(gè)小結(jié):
確定何時(shí)進(jìn)行文件合并,考慮因素可以是文件數(shù)量、文件大小。如果存在分區(qū),還要考慮到分區(qū)的變更。
為節(jié)約存儲(chǔ)空間和費(fèi)用,確保刪除未合并的文件。
盡可能調(diào)大合并文件的大小,同時(shí)解壓后內(nèi)存能夠容得下。
盡可能避免文件合并作業(yè)和流作業(yè)提交時(shí)的鎖爭(zhēng)用和沖突。
總結(jié)
以上是生活随笔為你收集整理的数据湖常用查询优化技术的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java okhttp 实现对有道翻译的
- 下一篇: ABAC权限模型的设计