如何根據(jù)Spark SQL explaind中的統(tǒng)計(jì)信息深入了解CBO優(yōu)化,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),綏芬河企業(yè)網(wǎng)站建設(shè),綏芬河品牌網(wǎng)站建設(shè),網(wǎng)站定制,綏芬河網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,綏芬河網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
Spark SQL 優(yōu)化器使用兩種優(yōu)化方式:基于規(guī)則的和基于代價(jià)的。前者依賴(lài)于啟發(fā)式規(guī)則,而后者依賴(lài)于數(shù)據(jù)的統(tǒng)計(jì)屬性。在這篇文章里,我們解釋一下在底層這些統(tǒng)計(jì)信息是怎么被用到,以及哪些場(chǎng)景下是有用的,并且怎么來(lái)使用這些統(tǒng)計(jì)信息。
大部分基于啟發(fā)式的優(yōu)化規(guī)則都沒(méi)有考慮到被處理的數(shù)據(jù)屬性。比如:基于啟發(fā)式的PredicatePushDown規(guī)則就是基于先過(guò)濾再計(jì)算的假設(shè)。
然而有些場(chǎng)景spark能夠通過(guò)數(shù)據(jù)的統(tǒng)計(jì)信息來(lái)得出更好的計(jì)劃,這通常被稱(chēng)作基于代價(jià)的優(yōu)化或者CBO,我們來(lái)探討一下細(xì)節(jié)。
為了能夠看到一個(gè)表的統(tǒng)計(jì)信息首先我們需要通過(guò)運(yùn)行sql語(yǔ)句來(lái)計(jì)算(所有的SQL語(yǔ)句可以通過(guò)使用sql()函數(shù)來(lái)指定,spark.sql(需要指定的sql字符串)):
ANALYZE TABLE table_name COMPUTE STATISTICS
運(yùn)行完這個(gè)以后,表級(jí)別的統(tǒng)計(jì)信息就會(huì)統(tǒng)計(jì)出來(lái)并且被存儲(chǔ)在元數(shù)據(jù)中,我們可以通過(guò)以下語(yǔ)句來(lái)查看:
DESCRIBE EXTENDED table_name
這將會(huì)展現(xiàn)一些表屬性以及表級(jí)別的統(tǒng)計(jì)信息。這有兩種維度信息:rowCount和sizeBytes:
除了表級(jí)別的統(tǒng)計(jì)信息,這也有列級(jí)別的統(tǒng)計(jì)信息,我們可以通過(guò)一下語(yǔ)句去計(jì)算和查看:
ANALYZE TABLE table_name COMPUTE STATISTICS FOR COLUMNS col_name DESCRIBE EXTENDED table_name column_name
這將展示給我們類(lèi)似一下的表(在這個(gè)例子中我們使用的列是user_id):
就像你看到的,這里有各種各樣的列維度信息,像最大最大最小值,null值的數(shù)量,去重的值的數(shù)量 (近似值)等。 從Spark 3.0以來(lái),這里有更多的選項(xiàng)去展示這些信息,能夠展示的不僅僅是表也包括了實(shí)際的查詢(xún)語(yǔ)句。可以通過(guò)explain的的mode參數(shù)來(lái)實(shí)現(xiàn):
spark.table(table_name).explain(mode='cost')
這個(gè)將會(huì)給我們展示兩種查詢(xún)計(jì)劃,物理計(jì)劃和優(yōu)化的邏輯計(jì)劃,該邏輯計(jì)劃將會(huì)展示一些統(tǒng)計(jì)信息,就像以下圖片展示的:
這個(gè)重要的一點(diǎn)是你能看到計(jì)劃的每個(gè)操作的的統(tǒng)計(jì)信息,所以在各種各樣的轉(zhuǎn)變之后你能看到統(tǒng)計(jì)信息的估算。這些統(tǒng)計(jì)信息首先通過(guò)Relation操作也就是所謂的葉子節(jié)點(diǎn)計(jì)算出來(lái)的,并且每個(gè)葉子節(jié)點(diǎn)都負(fù)責(zé)計(jì)算,后續(xù)經(jīng)過(guò)一些規(guī)則通過(guò)邏輯計(jì)劃進(jìn)行傳播。
接下來(lái),我們將會(huì)了解葉子節(jié)點(diǎn)是這么計(jì)算統(tǒng)計(jì)信息,以及怎么傳播的。
葉子節(jié)點(diǎn)計(jì)算統(tǒng)計(jì)信息有三種方式:第一種也是最好的一種是從元數(shù)據(jù)中獲取的統(tǒng)計(jì)信息。第二種是spark將會(huì)使用InMemoryFileIndex,他將會(huì)調(diào)用底層的 Hadoop API去收集數(shù)據(jù)源中的每個(gè)文件的的大小并且求和得到總值sizeInBytes(這里只有sizeInBytes度量會(huì)被計(jì)算到),最后一種方式是spark將會(huì)使用默認(rèn)的sizeInBytes維度值,該值由spark.sql.defaultSizeInBytes配置 并且該默認(rèn)值為8EIB,所以基本上spark對(duì)于Relation sizeInBytes將會(huì)盡可能的進(jìn)行重新計(jì)算覆蓋。(這也是只有sizeInBytes這種度量用到),這三種方式可以通過(guò)一下圖表進(jìn)行描述:
這個(gè)圖標(biāo)是一個(gè)樹(shù)形,每個(gè)節(jié)點(diǎn)是一條件,假如條件為真,我們將轉(zhuǎn)到T,否則轉(zhuǎn)到F。葉子將會(huì)代表統(tǒng)計(jì)信息將會(huì)計(jì)算的實(shí)際方式。例如:InMemoryFI 意味著只有sizeInBytes將調(diào)用Hadoop API進(jìn)行計(jì)算。另一方面,Stats From M 意味著統(tǒng)計(jì)信息將會(huì)從元數(shù)據(jù)獲得,然而在左邊的數(shù) 所有統(tǒng)計(jì)信息將會(huì)從元數(shù)據(jù)獲取,而右邊只有metricsInBytes維度將會(huì)被獲取。葉子節(jié)點(diǎn)CatalogFileIndex 代表著最后一種方法-默認(rèn)值為8EIB的sizeInBytes將會(huì)被使用到。
在圖表中,我們有四種條件,第一種決定了統(tǒng)計(jì)信息怎么被獲取:假如我們讀取數(shù)據(jù)作為一個(gè)表df=spark.table(table_name),那我們就進(jìn)入到左邊,否則進(jìn)入到右邊,下一個(gè)條件是 是否基于代價(jià)的優(yōu)化(CBO)是否開(kāi)啟,這個(gè)通過(guò)spark.sql.cbo.enabled配置,默認(rèn)值是false(到spark 3.0.0).第三個(gè)條件是在元數(shù)據(jù)的統(tǒng)計(jì)信息是否通過(guò)analyzed table command(ATC)計(jì)算出來(lái)的,最后一個(gè)是表是否分區(qū)。 最好的情況是 我們讀取數(shù)據(jù)作為一個(gè)表,CBO是開(kāi)啟的,而且已經(jīng)運(yùn)行了ATC,這種情況下,所有的統(tǒng)計(jì)信息將會(huì)從元數(shù)據(jù)中獲取(除了從rowCount計(jì)算的sizeInBytes),另一個(gè)方面,最壞的情況是,我們讀取數(shù)據(jù)作為一個(gè)表,但是ATC沒(méi)有運(yùn)行,而且表是分區(qū)的,這種情況下默認(rèn)的sizeInBytes將會(huì)從配置中讀取,并且計(jì)算是很不精確的,注意最壞的情況跟CBO是否開(kāi)啟是無(wú)關(guān)的。注意一點(diǎn):假如表不是分區(qū)的,spark將會(huì)使用Hadoop API計(jì)算sizeInBytes,所以表是否分區(qū)直接影響了葉子節(jié)點(diǎn)的統(tǒng)計(jì)信息被計(jì)算的方式。
一旦葉子節(jié)點(diǎn)的統(tǒng)計(jì)信息被計(jì)算出來(lái),該統(tǒng)計(jì)信息會(huì)被傳播到其他節(jié)點(diǎn)。這里有兩種傳播方式:第一種(我們稱(chēng)之為老方式)是非常基本的而且只有一種維度sizeInBytes被傳播,并且在各種操作中該維度被調(diào)整的的方式也是很基本的。例如,F(xiàn)ilter操作并不調(diào)整sizeInBytes的值,如下所示:
(
spark.table(table_name)
.filter(col("user_id") < 0)
).explain(mode="cost")在這個(gè)查詢(xún)中,我們過(guò)濾除了所有user_id是負(fù)數(shù)的記錄,實(shí)際上是沒(méi)有該記錄的,但是spark并不能獲取這種信息,因?yàn)檫@種需要列級(jí)別的統(tǒng)計(jì)信息,這再老方式中不會(huì)被使用到。所以從這查詢(xún)計(jì)劃中可以看到,只有sizeInBytes被傳播,并且在兩個(gè)操作中該值保持不變.
第二種統(tǒng)計(jì)信息傳播的方式更加成熟,從spark 2.2開(kāi)始但是它要求開(kāi)啟CBO,而且要求通過(guò)ATC讓元數(shù)據(jù)儲(chǔ)存統(tǒng)計(jì)信息。這個(gè)時(shí)候所有的信息都會(huì)被傳播,加入我們提供了列級(jí)別的維度,spark將會(huì)將會(huì)計(jì)算filter操作,并且計(jì)算出一個(gè)更好的統(tǒng)計(jì)信息:
如你所見(jiàn),在fiter操作的統(tǒng)計(jì)信息被改變了,rowCount非零,sizeInBytes 是1B,是最小值,從這個(gè)user_id列級(jí)別的統(tǒng)計(jì)信息,spark能夠知道負(fù)user_id的記錄是存在的,這個(gè)在查詢(xún)計(jì)劃中可以反映出來(lái)。
在這種新方式中,為了計(jì)算sizeInBytes,spark首先根據(jù)每個(gè)數(shù)據(jù)類(lèi)型計(jì)算出單行的大小,之后再乘以rowCount去得到最終的sizeInBytes。假如rowCount是零,則sizeInBytes將會(huì)設(shè)置為1去避免在其他統(tǒng)計(jì)算的除零問(wèn)題。這也適用于project操作(spark知道哪些列將會(huì)被投影,所以需要提前計(jì)算出單行的大小)
此時(shí)我們已經(jīng)知道了統(tǒng)計(jì)信息怎么被計(jì)算的以及怎么通過(guò)邏輯計(jì)劃傳播的,現(xiàn)在讓我們來(lái)看一下在查詢(xún)計(jì)劃中怎么被使用以獲取更優(yōu)的計(jì)劃。
這有兩個(gè)地方統(tǒng)計(jì)信息會(huì)被使用:第一個(gè)是JoinSelection策略,這里spark將會(huì)決定使用哪種算法進(jìn)行join兩個(gè)DataFrame(更多的細(xì)節(jié)參考 這里。基本的邏輯就是假如一個(gè)df小于某個(gè)閾值,spark將會(huì)使用BraodcastHashJoin(BHJ),因?yàn)榧偃绫粡V播的df如果很小的話(huà),這將是一個(gè)非常有效的方式。這個(gè)閾值通過(guò)spark.sql.autoBroadcastJoinThreshold 配置,默認(rèn)是10MB,所以對(duì)于df的大小有個(gè)很好的預(yù)估的話(huà),能夠幫助我們選擇一個(gè)更好的join優(yōu)化短發(fā)。
第二個(gè)地方也是跟join相關(guān),即joinRecorder規(guī)則,使用這個(gè)規(guī)則 spark將會(huì)找到j(luò)oin操作最優(yōu)化的順序(如果你join多于兩個(gè)表的話(huà))。這個(gè)規(guī)則默認(rèn)是關(guān)閉的,假如你想開(kāi)啟的話(huà),通過(guò)如下配置:
spark.conf.set("spark.sql.cbo.joinReorder.enabled",True)我們可以通過(guò)一下屬性控制df的最大數(shù)量:
spark.conf.set("spark.sql.cbo.joinReorder.dp.threshold",n)n的默認(rèn)值是12。
我們已經(jīng)知道假如一個(gè)表是分區(qū)的,并且我們沒(méi)有運(yùn)行ATC,spark將會(huì)使用默認(rèn)的值 8EIB,這是很大的。所以在我們join很多表并且這些表是分區(qū)且十分小的情況下,他們是可以進(jìn)行BHJ的,并且運(yùn)行ATC是有意義的。當(dāng)然我們必須清楚,加入一個(gè)表的數(shù)據(jù)被追加或者被覆蓋了,之前的統(tǒng)計(jì)信息就會(huì)被刪除,所以我們必須重新運(yùn)行ATC。在某些情況下,更新元數(shù)據(jù)的統(tǒng)計(jì)信息是比較復(fù)雜的。一個(gè)解決方法是利用自適應(yīng)查詢(xún)-spark 3.0的新特性。
在spark 3.0 自適應(yīng)查詢(xún)(AQE)這個(gè)新特性被開(kāi)發(fā),它將會(huì)以一種更加高級(jí)的方式使用統(tǒng)計(jì)信息。假如開(kāi)啟了AQE(默認(rèn)不開(kāi)啟),在每個(gè)stage執(zhí)行完后,統(tǒng)計(jì)信息會(huì)被重新計(jì)算。這就可以獲取更加精確的統(tǒng)計(jì)信息,以便能夠決定是否使用BHJ,AQE自身是一個(gè)很大的主題,我們分幾篇文章來(lái)介紹它。
看完上述內(nèi)容,你們掌握如何根據(jù)Spark SQL explaind中的統(tǒng)計(jì)信息深入了解CBO優(yōu)化的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
文章題目:如何根據(jù)SparkSQLexplaind中的統(tǒng)計(jì)信息深入了解CBO優(yōu)化
瀏覽路徑:http://chinadenli.net/article34/ppsdse.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、虛擬主機(jī)、關(guān)鍵詞優(yōu)化、網(wǎng)站建設(shè)、網(wǎng)站收錄、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)