2022年最強大資料面試寶典(全文50000字,強烈建議收藏)

韓楠發表於2022-08-25

此套面試題來自於各大廠的真實面試題及常問的知識點,如果能理解吃透這些問題,你的大資料能力將會大大提升,進入大廠指日可待。

複習大資料面試題,看這一套就夠了!

本文目錄:

一、Hadoop
二、Hive
三、Spark
四、Kafka
五、HBase
六、Flink
七、數倉業務方面
八、演算法

Hadoop

Hadoop中常問的就三塊,第一:分散式儲存(HDFS);第二:分散式計算框架(MapReduce);第三:資源排程框架(YARN)。

1. 請說下HDFS讀寫流程

這個問題雖然見過無數次,面試官問過無數次,還是有不少面試者不能完整的說出來,所以請務必記住。並且很多問題都是從HDFS讀寫流程中引申出來的。

HDFS寫流程

  1. Client客戶端傳送上傳請求, 透過RPC與NameNode建立通訊,NameNode檢查該使用者是否有上傳許可權,以及上傳的檔案是否在HDFS對應的目錄下重名,如果這兩者有任意一個不滿足,則直接報錯,如果兩者都滿足,則返回給客戶端一個可以上傳的資訊;

  2. Client根據檔案的大小進行切分,預設128M一塊,切分完成之後給NameNode傳送請求第一個block塊上傳到哪些伺服器上;

  3. NameNode收到請求之後,根據網路拓撲和機架感知以及副本機制進行檔案分配,返回可用的DataNode的地址;

注:Hadoop在設計時考慮到資料的安全與高效,  資料檔案預設在HDFS上存放三份, 儲存策略為本地一份,同機架內其它某一節點上一份, 不同機架的某一節點上一份

  1. 客戶端收到地址之後與伺服器地址列表中的一個節點如A進行通訊,本質上就是RPC呼叫,建立pipeline,A收到請求後會繼續呼叫B,B在呼叫C,將整個pipeline建立完成,逐級返回Client;

  2. Client開始向A上傳送第一個block( 先從磁碟讀取資料然後放到本地記憶體快取), 以packet(資料包,64kb)為單位,A收到一個packet就會傳送給B,然後B傳送給C,A每傳完一個packet就會放入一個應答佇列等待應答

  3. 資料被分割成一個個的packet資料包在pipeline上依次傳輸, 在pipeline反向傳輸中,逐個傳送ack(命令正確應答),最終由pipeline中第一個DataNode節點A將pipelineack傳送給Client;

  4. 當一個block傳輸完成之後, Client再次請求NameNode上傳第二個block,NameNode重新選擇三臺DataNode給Client。

HDFS讀流程

  1. Client向NameNode傳送RPC請求。請求檔案block的位置;

  2. NameNode收到請求之後會檢查使用者許可權以及是否有這個檔案,如果都符合,則會視情況返回部分或全部的block列表,對於每個block,NameNode都會返回含有該block副本的DataNode地址;這些返回的DataNode地址,會按照叢集拓撲結構得出DataNode與客戶端的距離,然後進行排序, 排序兩個規則:網路拓撲結構中距離 Client 近的排靠前;心跳機制中超時彙報的DataNode狀態為STALE,這樣的排靠後;

  3. Client選取排序靠前的DataNode來讀取block,如果客戶端本身就是DataNode,那麼將從本地直接獲取資料( 短路讀取特性);

  4. 底層上本質是建立Socket Stream(FSDataInputStream),重複的呼叫父類DataInputStream的read方法,直到這個塊上的資料讀取完畢;

  5. 當讀完列表的block後,若檔案讀取還沒有結束,客戶端會繼續向NameNode 獲取下一批的block列表;

  6. 讀取完一個block都會進行checksum驗證,如果讀取DataNode時出現錯誤,客戶端會通知NameNode,然後再從下一個擁有該block副本的DataNode 繼續讀;

  7. read方法是並行的讀取block資訊,不是一塊一塊的讀取;NameNode只是返回Client請求包含塊的DataNode地址, 並不是返回請求塊的資料

  8. 最終讀取來所有的block會合併成一個完整的最終檔案;

2. HDFS在讀取檔案的時候,如果其中一個塊突然損壞了怎麼辦

客戶端讀取完DataNode上的塊之後會進行checksum驗證,也就是把客戶端讀取到本地的塊與HDFS上的原始塊進行校驗,如果發現校驗結果不一致,客戶端會通知NameNode,然後再 從下一個擁有該block副本的DataNode繼續讀

3. HDFS在上傳檔案的時候,如果其中一個DataNode突然掛掉了怎麼辦

客戶端上傳檔案時與DataNode建立pipeline管道,管道的正方向是客戶端向DataNode傳送的資料包,管道反向是DataNode向客戶端傳送ack確認,也就是正確接收到資料包之後傳送一個已確認接收到的應答。

當DataNode突然掛掉了,客戶端接收不到這個DataNode傳送的ack確認,客戶端會通知NameNode,NameNode檢查該塊的副本與規定的不符,NameNode會通知DataNode去複製副本,並將掛掉的DataNode作下線處理,不再讓它參與檔案上傳與下載。

4. NameNode在啟動的時候會做哪些操作

NameNode資料儲存在記憶體和本地磁碟,本地磁碟資料儲存在 fsimage映象檔案和edits編輯日誌檔案

首次啟動NameNode

  1. 格式化檔案系統,為了生成fsimage映象檔案

  2. 啟動NameNode:

  • 讀取fsimage檔案,將檔案內容載入進記憶體
  • 等待DataNade註冊與傳送block report
  • 啟動DataNode:

    • 向NameNode註冊
    • 傳送block report
    • 檢查fsimage中記錄的塊的數量和block report中的塊的總數是否相同
  • 對檔案系統進行操作(建立目錄,上傳檔案,刪除檔案等):

    • 此時記憶體中已經有檔案系統改變的資訊,但是磁碟中沒有檔案系統改變的資訊,此時會將這些改變資訊寫入edits檔案中,edits檔案中儲存的是檔案系統後設資料改變的資訊。

    第二次啟動NameNode

    1. 讀取fsimage和edits檔案;

    2. 將fsimage和edits檔案合併成新的fsimage檔案;

    3. 建立新的edits檔案,內容開始為空;

    4. 啟動DataNode。

    5. Secondary NameNode瞭解嗎,它的工作機制是怎樣的

    Secondary NameNode是合併NameNode的edit logs到fsimage檔案中;

    它的具體工作機制:

    1. Secondary NameNode詢問NameNode是否需要checkpoint。直接帶回NameNode是否檢查結果;

    2. Secondary NameNode請求執行checkpoint;

    3. NameNode滾動正在寫的edits日誌;

    4. 將滾動前的編輯日誌和映象檔案複製到Secondary NameNode;

    5. Secondary NameNode載入編輯日誌和映象檔案到記憶體,併合並;

    6. 生成新的映象檔案fsimage.chkpoint;

    7. 複製fsimage.chkpoint到NameNode;

    8. NameNode將fsimage.chkpoint重新命名成fsimage;

    所以如果NameNode中的後設資料丟失,是可以從Secondary NameNode恢復一部分後設資料資訊的,但不是全部,因為NameNode正在寫的edits日誌還沒有複製到Secondary NameNode,這部分恢復不了。

    6. Secondary NameNode不能恢復NameNode的全部資料,那如何保證NameNode資料儲存安全

    這個問題就要說NameNode的高可用了,即  NameNode HA

    一個NameNode有單點故障的問題,那就配置雙NameNode,配置有兩個關鍵點,一是必須要保證這兩個NameNode的後設資料資訊必須要同步的,二是一個NameNode掛掉之後另一個要立馬補上。

    1. 後設資料資訊同步在 HA 方案中採用的是“共享儲存”。每次寫檔案時,需要將日誌同步寫入共享儲存,這個步驟成功才能認定寫檔案成功。然後備份節點定期從共享儲存同步日誌,以便進行主備切換。

    2. 監控NameNode狀態採用zookeeper,兩個NameNode節點的狀態存放在zookeeper中,另外兩個NameNode節點分別有一個程式監控程式,實施讀取zookeeper中有NameNode的狀態,來判斷當前的NameNode是不是已經down機。如果Standby的NameNode節點的ZKFC發現主節點已經掛掉,那麼就會強制給原本的Active NameNode節點傳送強制關閉請求,之後將備用的NameNode設定為Active。

    如果面試官再問HA中的 共享儲存 是怎麼實現的知道嗎?
    可以進行解釋下:NameNode 共享儲存方案有很多,比如Linux HA, VMware FT, QJM等,目前社群已經把由Clouderea公司實現的基於QJM(Quorum Journal Manager)的方案合併到HDFS的trunk之中並且作為 預設的共享儲存實現。
    基於QJM的共享儲存系統 主要用於儲存EditLog,並不儲存FSImage檔案。FSImage檔案還是在NameNode的本地磁碟上。
    QJM共享儲存的基本思想來自於Paxos演算法,採用多個稱為JournalNode的節點組成的JournalNode叢集來儲存EditLog。每個JournalNode儲存同樣的EditLog副本。每次NameNode寫EditLog的時候,除了向本地磁碟寫入 EditLog 之外,也會並行地向JournalNode叢集之中的每一個JournalNode傳送寫請求,只要大多數的JournalNode節點返回成功就認為向JournalNode叢集寫入EditLog成功。如果有2N+1臺JournalNode,那麼根據大多數的原則,最多可以容忍有N臺JournalNode節點掛掉。

    7. 在NameNode HA中,會出現腦裂問題嗎?怎麼解決腦裂

    假設 NameNode1 當前為 Active 狀態,NameNode2 當前為 Standby 狀態。如果某一時刻 NameNode1 對應的 ZKFailoverController 程式發生了“假死”現象,那麼 Zookeeper 服務端會認為 NameNode1 掛掉了,根據前面的主備切換邏輯,NameNode2 會替代 NameNode1 進入 Active 狀態。但是此時 NameNode1 可能仍然處於 Active 狀態正常執行,這樣 NameNode1 和 NameNode2 都處於 Active 狀態,都可以對外提供服務。這種情況稱為腦裂。

    腦裂對於NameNode這類對資料一致性要求非常高的系統來說是災難性的,資料會發生錯亂且無法恢復。zookeeper社群對這種問題的解決方法叫做 fencing,中文翻譯為隔離,也就是想辦法把舊的 Active NameNode 隔離起來,使它不能正常對外提供服務。

    在進行 fencing 的時候,會執行以下的操作:

    1. 首先嚐試呼叫這個舊 Active NameNode 的 HAServiceProtocol RPC 介面的 transitionToStandby 方法,看能不能把它轉換為 Standby 狀態。

    2. 如果 transitionToStandby 方法呼叫失敗,那麼就執行 Hadoop 配置檔案之中預定義的隔離措施,Hadoop 目前主要提供兩種隔離措施,通常會選擇 sshfence:

    • sshfence:透過 SSH 登入到目標機器上,執行命令 fuser 將對應的程式殺死;
    • shellfence:執行一個使用者自定義的 shell 指令碼來將對應的程式隔離。

    8. 小檔案過多會有什麼危害,如何避免

    Hadoop上大量HDFS後設資料資訊儲存在NameNode記憶體中,因此過多的小檔案必定會壓垮NameNode的記憶體。

    每個後設資料物件約佔150byte,所以如果有1千萬個小檔案,每個檔案佔用一個block,則NameNode大約需要2G空間。如果儲存1億個檔案,則NameNode需要20G空間。

    顯而易見的解決這個問題的方法就是合併小檔案,可以選擇在客戶端上傳時執行一定的策略先合併,或者是使用Hadoop的 CombineFileInputFormat\<K,V\>實現小檔案的合併。

    9. 請說下HDFS的組織架構

    1. Client:客戶端

    • 切分檔案。檔案上傳HDFS的時候,Client將檔案切分成一個一個的Block,然後進行儲存

    • 與NameNode互動,獲取檔案的位置資訊

    • 與DataNode互動,讀取或者寫入資料

    • Client提供一些命令來管理HDFS,比如啟動關閉HDFS、訪問HDFS目錄及內容等

  • NameNode:名稱節點,也稱主節點,儲存資料的後設資料資訊,不儲存具體的資料

    • 管理HDFS的名稱空間

    • 管理資料塊(Block)對映資訊

    • 配置副本策略

    • 處理客戶端讀寫請求

  • DataNode:資料節點,也稱從節點。NameNode下達命令,DataNode執行實際的操作

    • 儲存實際的資料塊

    • 執行資料塊的讀/寫操作

  • Secondary NameNode:並非NameNode的熱備。當NameNode掛掉的時候,它並不能馬上替換NameNode並提供服務

    • 輔助NameNode,分擔其工作量

    • 定期合併Fsimage和Edits,並推送給NameNode

    • 在緊急情況下,可輔助恢復NameNode

    10. 請說下MR中Map Task的工作機制

    簡單概述

    inputFile透過split被切割為多個split檔案,透過Record按行讀取內容給map(自己寫的處理邏輯的方法) ,資料被map處理完之後交給OutputCollect收集器,對其結果key進行分割槽(預設使用的hashPartitioner),然後寫入buffer, 每個map task 都有一個記憶體緩衝區(環形緩衝區),存放著map的輸出結果,當緩衝區快滿的時候需要將緩衝區的資料以一個臨時檔案的方式溢寫到磁碟,當整個map task 結束後再對磁碟中這個maptask產生的所有臨時檔案做合併,生成最終的正式輸出檔案,然後等待reduce task的拉取。

    詳細步驟

    1. 讀取資料元件 InputFormat (預設 TextInputFormat) 會透過 getSplits 方法對輸入目錄中的檔案進行邏輯切片規劃得到 block,有多少個 block就對應啟動多少個 MapTask。

    2. 將輸入檔案切分為 block 之後,由 RecordReader 物件 (預設是LineRecordReader) 進行讀取,以 \n 作為分隔符, 讀取一行資料, 返回 <key,value>, Key 表示每行首字元偏移值,Value 表示這一行文字內容。

    3. 讀取 block 返回 <key,value>, 進入使用者自己繼承的 Mapper 類中,執行使用者重寫的 map 函式,RecordReader 讀取一行這裡呼叫一次。

    4. Mapper 邏輯結束之後,將 Mapper 的每條結果透過 context.write 進行collect資料收集。在 collect 中,會先對其進行分割槽處理,預設使用 HashPartitioner。

    5. 接下來,會將資料寫入記憶體,記憶體中這片區域叫做環形緩衝區(預設100M),緩衝區的作用是 批次收集 Mapper 結果,減少磁碟 IO 的影響。我們的 Key/Value 對以及 Partition 的結果都會被寫入緩衝區。當然,寫入之前,Key 與 Value 值都會被序列化成位元組陣列

    6. 當環形緩衝區的資料達到溢寫比列(預設0.8),也就是80M時,溢寫執行緒啟動, 需要對這 80MB 空間內的 Key 做排序 (Sort)。排序是 MapReduce 模型預設的行為,這裡的排序也是對序列化的位元組做的排序。

    7. 合併溢寫檔案,每次溢寫會在磁碟上生成一個臨時檔案 (寫之前判斷是否有 Combiner),如果 Mapper 的輸出結果真的很大,有多次這樣的溢寫發生,磁碟上相應的就會有多個臨時檔案存在。當整個資料處理結束之後開始對磁碟中的臨時檔案進行 Merge 合併,因為最終的檔案只有一個寫入磁碟,並且為這個檔案提供了一個索引檔案,以記錄每個reduce對應資料的偏移量。

    11. 請說下MR中Reduce Task的工作機制

    簡單描述

    Reduce 大致分為 copy、sort、reduce 三個階段,重點在前兩個階段。

    copy 階段包含一個 eventFetcher 來獲取已完成的 map 列表,由 Fetcher 執行緒去 copy 資料,在此過程中會啟動兩個 merge 執行緒,分別為 inMemoryMerger 和 onDiskMerger,分別將記憶體中的資料 merge 到磁碟和將磁碟中的資料進行 merge。待資料 copy 完成之後,copy 階段就完成了。

    開始進行 sort 階段,sort 階段主要是執行 finalMerge 操作,純粹的 sort 階段,完成之後就是 reduce 階段,呼叫使用者定義的 reduce 函式進行處理。

    詳細步驟

    1. Copy階段:簡單地拉取資料。Reduce程式啟動一些資料copy執行緒(Fetcher),透過HTTP方式請求maptask獲取屬於自己的檔案(map task 的分割槽會標識每個map task屬於哪個reduce task ,預設reduce task的標識從0開始)。

    2. Merge階段:在遠端複製資料的同時,ReduceTask啟動了兩個後臺執行緒對記憶體和磁碟上的檔案進行合併,以防止記憶體使用過多或磁碟上檔案過多。

      merge有三種形式:記憶體到記憶體;記憶體到磁碟;磁碟到磁碟。預設情況下第一種形式不啟用。當記憶體中的資料量到達一定閾值,就直接啟動記憶體到磁碟的merge。與map端類似,這也是溢寫的過程,這個過程中如果你設定有Combiner,也是會啟用的,然後在磁碟中生成了眾多的溢寫檔案。記憶體到磁碟的merge方式一直在執行,直到沒有map端的資料時才結束,然後啟動第三種磁碟到磁碟的merge方式生成最終的檔案。

    3. 合併排序:把分散的資料合併成一個大的資料後,還會再對合並後的資料排序。

    4. 對排序後的鍵值對呼叫reduce方法:鍵相等的鍵值對呼叫一次reduce方法,每次呼叫會產生零個或者多個鍵值對,最後把這些輸出的鍵值對寫入到HDFS檔案中。

    12. 請說下MR中Shuffle階段

    shuffle階段分為四個步驟:依次為:分割槽,排序,規約,分組,其中前三個步驟在map階段完成,最後一個步驟在reduce階段完成。

    shuffle 是 Mapreduce 的核心,它分佈在 Mapreduce 的 map 階段和 reduce 階段。一般把從 Map 產生輸出開始到 Reduce 取得資料作為輸入之前的過程稱作 shuffle。

    1. Collect階段:將 MapTask 的結果輸出到預設大小為 100M 的環形緩衝區,儲存的是 key/value,Partition 分割槽資訊等。

    2. Spill階段:當記憶體中的資料量達到一定的閥值的時候,就會將資料寫入本地磁碟,在將資料寫入磁碟之前需要對資料進行一次排序的操作,如果配置了 combiner,還會將有相同分割槽號和 key 的資料進行排序。

    3. MapTask階段的Merge:把所有溢位的臨時檔案進行一次合併操作,以確保一個 MapTask 最終只產生一箇中間資料檔案。

    4. Copy階段:ReduceTask 啟動 Fetcher 執行緒到已經完成 MapTask 的節點上覆制一份屬於自己的資料,這些資料預設會儲存在記憶體的緩衝區中,當記憶體的緩衝區達到一定的閥值的時候,就會將資料寫到磁碟之上。

    5. ReduceTask階段的Merge:在 ReduceTask 遠端複製資料的同時,會在後臺開啟兩個執行緒對記憶體到本地的資料檔案進行合併操作。

    6. Sort階段:在對資料進行合併的同時,會進行排序操作,由於 MapTask 階段已經對資料進行了區域性的排序,ReduceTask 只需保證 Copy 的資料的最終整體有效性即可。

    Shuffle 中的緩衝區大小會影響到 mapreduce 程式的執行效率,原則上說,緩衝區越大,磁碟io的次數越少,執行速度就越快。
    緩衝區的大小可以透過引數調整,  引數: mapreduce.task.io.sort.mb  預設100M

    13. Shuffle階段的資料壓縮機制瞭解嗎

    在shuffle階段,可以看到資料透過大量的複製,從map階段輸出的資料,都要透過網路複製,傳送到reduce階段,這一過程中,涉及到大量的網路IO,如果資料能夠進行壓縮,那麼資料的傳送量就會少得多。

    hadoop當中支援的壓縮演算法:
    gzip、bzip2、LZO、LZ4、 Snappy,這幾種壓縮演算法綜合壓縮和解壓縮的速率,谷歌的Snappy是最優的,一般都選擇Snappy壓縮。谷歌出品,必屬精品。

    14. 在寫MR時,什麼情況下可以使用規約

    規約(combiner)是不能夠影響任務的執行結果的區域性彙總,適用於求和類,不適用於求平均值,如果reduce的輸入引數型別和輸出引數的型別是一樣的,則規約的類可以使用reduce類,只需要在驅動類中指明規約的類即可。

    15. YARN叢集的架構和工作原理知道多少

    YARN的基本設計思想是將MapReduce V1中的JobTracker拆分為兩個獨立的服務:ResourceManager和ApplicationMaster。

    ResourceManager負責整個系統的資源管理和分配,ApplicationMaster負責單個應用程式的的管理。

    1. ResourceManager:RM是一個全域性的資源管理器,負責整個系統的資源管理和分配,它主要由兩個部分組成:排程器(Scheduler)和應用程式管理器(Application Manager)。

    排程器根據容量、佇列等限制條件,將系統中的資源分配給正在執行的應用程式,在保證容量、公平性和服務等級的前提下,最佳化叢集資源利用率,讓所有的資源都被充分利用應用程式管理器負責管理整個系統中的所有的應用程式,包括應用程式的提交、與排程器協商資源以啟動ApplicationMaster、監控ApplicationMaster執行狀態並在失敗時重啟它。

    1. ApplicationMaster:使用者提交的一個應用程式會對應於一個ApplicationMaster,它的主要功能有:

    • 與RM排程器協商以獲得資源,資源以Container表示。

    • 將得到的任務進一步分配給內部的任務。

    • 與NM通訊以啟動/停止任務。

    • 監控所有的內部任務狀態,並在任務執行失敗的時候重新為任務申請資源以重啟任務。

  • NodeManager:NodeManager是每個節點上的資源和工作管理員,一方面,它會定期地向RM彙報本節點上的資源使用情況和各個Container的執行狀態;另一方面,他接收並處理來自AM的Container啟動和停止請求。

  • Container:Container是YARN中的資源抽象,封裝了各種資源。 一個應用程式會分配一個Container,這個應用程式只能使用這個Container中描述的資源。不同於MapReduceV1中槽位slot的資源封裝,Container是一個動態資源的劃分單位,更能充分利用資源。

  • 16. YARN的任務提交流程是怎樣的

    當jobclient向YARN提交一個應用程式後,YARN將分兩個階段執行這個應用程式:一是啟動ApplicationMaster;第二個階段是由ApplicationMaster建立應用程式,為它申請資源,監控執行直到結束。具體步驟如下:

    1. 使用者向YARN提交一個應用程式,並指定ApplicationMaster程式、啟動ApplicationMaster的命令、使用者程式。

    2. RM為這個應用程式分配第一個Container,並與之對應的NM通訊,要求它在這個Container中啟動應用程式ApplicationMaster。

    3. ApplicationMaster向RM註冊,然後拆分為內部各個子任務,為各個內部任務申請資源,並監控這些任務的執行,直到結束。

    4. AM採用輪詢的方式向RM申請和領取資源。

    5. RM為AM分配資源,以Container形式返回。

    6. AM申請到資源後,便與之對應的NM通訊,要求NM啟動任務。

    7. NodeManager為任務設定好執行環境,將任務啟動命令寫到一個指令碼中,並透過執行這個指令碼啟動任務。

    8. 各個任務向AM彙報自己的狀態和進度,以便當任務失敗時可以重啟任務。

    9. 應用程式完成後,ApplicationMaster向ResourceManager登出並關閉自己。

    17. YARN的資源排程三種模型瞭解嗎

    在Yarn中有三種排程器可以選擇:FIFO Scheduler ,Capacity Scheduler,Fair Scheduler。

    Apache版本的hadoop預設使用的是Capacity  Scheduler排程方式。CDH版本的預設使用的是Fair Scheduler排程方式

    FIFO Scheduler(先來先服務):

    FIFO Scheduler把應用按提交的順序排成一個佇列,這是一個先進先出佇列,在進行資源分配的時候,先給佇列中最頭上的應用進行分配資源,待最頭上的應用需求滿足後再給下一個分配,以此類推。

    FIFO Scheduler是最簡單也是最容易理解的排程器,也不需要任何配置,但它並不適用於共享叢集。大的應用可能會佔用所有叢集資源,這就導致其它應用被阻塞,比如有個大任務在執行,佔用了全部的資源,再提交一個小任務,則此小任務會一直被阻塞。

    Capacity Scheduler(能力排程器):

    對於Capacity排程器,有一個專門的佇列用來執行小任務,但是為小任務專門設定一個佇列會預先佔用一定的叢集資源,這就導致大任務的執行時間會落後於使用FIFO排程器時的時間。

    Fair Scheduler(公平排程器):

    在Fair排程器中,我們不需要預先佔用一定的系統資源,Fair排程器會為所有執行的job動態的調整系統資源。

    比如:當第一個大job提交時,只有這一個job在執行,此時它獲得了所有叢集資源;當第二個小任務提交後,Fair排程器會分配一半資源給這個小任務,讓這兩個任務公平的共享叢集資源。

    需要注意的是,在Fair排程器中,從第二個任務提交到獲得資源會有一定的延遲,因為它需要等待第一個任務釋放佔用的Container。小任務執行完成之後也會釋放自己佔用的資源,大任務又獲得了全部的系統資源。最終的效果就是Fair排程器即得到了高的資源利用率又能保證小任務及時完成。

    Hive

    1. Hive內部表和外部表的區別

    未被external修飾的是內部表,被external修飾的為外部表。

    區別

    1. 內部表資料由Hive自身管理,外部表資料由HDFS管理;

    2. 內部表資料儲存的位置是 hive.metastore.warehouse.dir(預設: /user/hive/warehouse),外部表資料的儲存位置由自己制定(如果沒有LOCATION,Hive將在HDFS上的 /user/hive/warehouse資料夾下以外部表的表名建立一個資料夾,並將屬於這個表的資料存放在這裡);

    3. 刪除內部表會直接刪除後設資料(metadata)及儲存資料;刪除外部表僅僅會刪除後設資料,HDFS上的檔案並不會被刪除

    2. Hive有索引嗎

    Hive支援索引(3.0版本之前),但是Hive的索引與關係型資料庫中的索引並不相同,比如,Hive不支援主鍵或者外來鍵。並且Hive索引提供的功能很有限,效率也並不高,因此Hive索引很少使用。

    • 索引適用的場景:

    適用於不更新的靜態欄位。以免總是重建索引資料。每次建立、更新資料後,都要重建索引以構建索引表。

    • Hive索引的機制如下:

    hive在指定列上建立索引,會產生一張索引表(Hive的一張物理表),裡面的欄位包括:索引列的值、該值對應的HDFS檔案路徑、該值在檔案中的偏移量。

    Hive 0.8版本後引入bitmap索引處理器,這個處理器適用於去重後,值較少的列(例如,某欄位的取值只可能是幾個列舉值) 因為索引是用空間換時間,索引列的取值過多會導致建立bitmap索引表過大。

    注意:Hive中每次有資料時需要及時更新索引,相當於重建一個新表,否則會影響資料查詢的效率和準確性, Hive官方文件已經明確表示Hive的索引不推薦被使用,在新版本的Hive中已經被廢棄了

    擴充套件:Hive是在0.7版本之後支援索引的,在0.8版本後引入bitmap索引處理器,在3.0版本開始移除索引的功能,取而代之的是2.3版本開始的物化檢視,自動重寫的物化檢視替代了索引的功能。

    3. 運維如何對Hive進行排程

    1. 將hive的sql定義在指令碼當中;

    2. 使用azkaban或者oozie進行任務的排程;

    3. 監控任務排程頁面。

    4. ORC、Parquet等列式儲存的優點

    ORC和Parquet都是高效能的儲存方式,這兩種儲存格式總會帶來儲存和效能上的提升。

    Parquet:

    1. Parquet支援巢狀的資料模型,類似於Protocol Buffers,每一個資料模型的schema包含多個欄位,每一個欄位有三個屬性:重複次數、資料型別和欄位名。
      重複次數可以是以下三種:required(只出現1次),repeated(出現0次或多次),optional(出現0次或1次)。每一個欄位的資料型別可以分成兩種:group(複雜型別)和primitive(基本型別)。

    2. Parquet中沒有Map、Array這樣的複雜資料結構,但是可以透過repeated和group組合來實現的。

    3. 由於Parquet支援的資料模型比較鬆散,可能一條記錄中存在比較深的巢狀關係,如果為每一條記錄都維護一個類似的樹狀結可能會佔用較大的儲存空間,因此Dremel論文中提出了一種高效的對於巢狀資料格式的壓縮演算法:Striping/Assembly演算法。透過Striping/Assembly演算法,parquet可以使用較少的儲存空間表示複雜的巢狀格式,並且通常Repetition level和Definition level都是較小的整數值,可以透過RLE演算法對其進行壓縮,進一步降低儲存空間。

    4. Parquet檔案是以二進位制方式儲存的,是不可以直接讀取和修改的,Parquet檔案是自解析的,檔案中包括該檔案的資料和後設資料。

    ORC:

    1. ORC檔案是自描述的,它的後設資料使用Protocol Buffers序列化,並且檔案中的資料儘可能的壓縮以降低儲存空間的消耗。

    2. 和Parquet類似,ORC檔案也是以二進位制方式儲存的,所以是不可以直接讀取,ORC檔案也是自解析的,它包含許多的後設資料,這些後設資料都是同構ProtoBuffer進行序列化的。

    3. ORC會盡可能合併多個離散的區間儘可能的減少I/O次數。

    4. ORC中使用了更加精確的索引資訊,使得在讀取資料時可以指定從任意一行開始讀取,更細粒度的統計資訊使得讀取ORC檔案跳過整個row group,ORC預設會對任何一塊資料和索引資訊使用ZLIB壓縮,因此ORC檔案佔用的儲存空間也更小。

    5. 在新版本的ORC中也加入了對Bloom Filter的支援,它可以進一 步提升謂詞下推的效率,在Hive 1.2.0版本以後也加入了對此的支 持。

    5. 資料建模用的哪些模型?

    1. 星型模型

    星型模式

    星型模式(Star Schema)是最常用的維度建模方式。星型模式是以事實表為中心,所有的維度表直接連線在事實表上,像星星一樣。星形模式的維度建模由一個事實表和一組維表成,且具有以下特點:

    a. 維表只和事實表關聯,維表之間沒有關聯;

    b. 每個維表主鍵為單列,且該主鍵放置在事實表中,作為兩邊連線的外來鍵;

    c. 以事實表為核心,維表圍繞核心呈星形分佈。

    2. 雪花模型

    雪花模式

    雪花模式(Snowflake Schema)是對星形模式的擴充套件。 雪花模式的維度表可以擁有其他維度表的,雖然這種模型相比星型更規範一些,但是由於這種模型不太容易理解,維護成本比較高,而且效能方面需要關聯多層維表,效能比星型模型要低。

    3. 星座模型

    星座模型

    星座模式是星型模式延伸而來,星型模式是基於一張事實表的,而 星座模式是基於多張事實表的,而且共享維度資訊。前面介紹的兩種維度建模方法都是多維表對應單事實表,但在很多時候維度空間內的事實表不止一個,而一個維表也可能被多個事實表用到。在業務發展後期,絕大部分維度建模都採用的是星座模式。

    數倉建模詳細介紹可檢視: 通俗易懂數倉建模

    6. 為什麼要對資料倉儲分層?

    • 用空間換時間,透過大量的預處理來提升應用系統的使用者體驗(效率),因此資料倉儲會存在大量冗餘的資料。

    • 如果不分層的話,如果源業務系統的業務規則發生變化將會影響整個資料清洗過程,工作量巨大。

    • 透過資料分層管理可以簡化資料清洗的過程,因為把原來一步的工作分到了多個步驟去完成,相當於把一個複雜的工作拆成了多個簡單的工作,把一個大的黑盒變成了一個白盒,每一層的處理邏輯都相對簡單和容易理解,這樣我們比較容易保證每一個步驟的正確性,當資料發生錯誤的時候,往往我們只需要區域性調整某個步驟即可。

    資料倉儲詳細介紹可檢視: 萬字詳解整個資料倉儲建設體系

    7. 使用過Hive解析JSON串嗎

    Hive處理json資料總體來說有兩個方向的路走

    1. 將json以字串的方式整個入Hive表,然後透過使用UDF函式解析已經匯入到hive中的資料,比如使用 LATERAL VIEW json_tuple的方法,獲取所需要的列名。

    2. 在匯入之前將json拆成各個欄位,匯入Hive表的資料是已經解析過的。這將需要使用第三方的 SerDe。

    詳細介紹可檢視: Hive解析Json陣列超全講解

    8. sort by 和 order by 的區別

    order by 會對輸入做全域性排序,因此只有一個reducer(多個reducer無法保證全域性有序)只有一個reducer,會導致當輸入規模較大時,需要較長的計算時間。

    sort by不是全域性排序,其在資料進入reducer前完成排序. 因此,如果用sort by進行排序,並且設定mapred.reduce.tasks>1, 則 sort by只保證每個reducer的輸出有序,不保證全域性有序

    9. 資料傾斜怎麼解決

    資料傾斜問題主要有以下幾種:

    1. 空值引發的資料傾斜

    2. 不同資料型別引發的資料傾斜

    3. 不可拆分大檔案引發的資料傾斜

    4. 資料膨脹引發的資料傾斜

    5. 表連線時引發的資料傾斜

    6. 確實無法減少資料量引發的資料傾斜

    以上傾斜問題的具體解決方案可檢視: Hive千億級資料傾斜解決方案

    注意:對於 left join 或者 right join 來說,不會對關聯的欄位自動去除null值,對於 inner join 來說,會對關聯的欄位自動去除null值。

    小夥伴們在閱讀時注意下,在上面的文章(Hive千億級資料傾斜解決方案)中,有一處sql出現了上述問題(舉例的時候原本是想使用left join的,結果手誤寫成了join)。此問題由公眾號讀者發現,感謝這位讀者指正。

    10. Hive 小檔案過多怎麼解決

    1. 使用 hive 自帶的 concatenate 命令,自動合併小檔案

    使用方法:

    
    #對於非分割槽表
    
    alter  table A concatenate;

    #對於分割槽表
    alter  table B  partition( day= 20201224) concatenate;

    注意:
    1、concatenate 命令只支援 RCFILE 和 ORC 檔案型別。
    2、使用concatenate命令合併小檔案時不能指定合併後的檔案數量,但可以多次執行該命令。
    3、當多次使用concatenate後檔案數量不在變化,這個跟引數 mapreduce.input.fileinputformat.split.minsize=256mb 的設定有關,可設定每個檔案的最小size。

    2. 調整引數減少Map數量

    設定map輸入合併小檔案的相關引數(執行Map前進行小檔案合併):

    在mapper中將多個檔案合成一個split作為輸入( CombineHiveInputFormat底層是Hadoop的 CombineFileInputFormat方法):

    
    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 
    -- 預設
    

    每個Map最大輸入大小(這個值決定了合併後檔案的數量):

    
    set mapred.max.split.size=
    256000000;   
    -- 256M
    

    一個節點上split的至少大小(這個值決定了多個DataNode上的檔案是否需要合併):

    
    set mapred.min.split.size.per.node=
    100000000;  
    -- 100M
    

    一個交換機下split的至少大小(這個值決定了多個交換機上的檔案是否需要合併):

    
    set mapred.min.split.size.per.rack=
    100000000;  
    -- 100M
    
    3. 減少Reduce的數量

    reduce 的個數決定了輸出的檔案的個數,所以可以調整reduce的個數控制hive表的檔案數量。

    hive中的分割槽函式 distribute by 正好是控制MR中partition分割槽的,可以透過設定reduce的數量,結合分割槽函式讓資料均衡的進入每個reduce即可:

    
    #設定reduce的數量有兩種方式,第一種是直接設定reduce個數
    
    set mapreduce.job.reduces= 10;

    #第二種是設定每個reduce的大小,Hive會根據資料總大小猜測確定一個reduce個數
    set hive.exec.reducers.bytes.per.reducer= 5120000000-- 預設是1G,設定為5G

    #執行以下語句,將資料均衡的分配到reduce中
    set mapreduce.job.reduces= 10;
    insert overwrite  table A  partition(dt)
    select *  from B
    distribute  by  rand();

    對於上述語句解釋:如設定reduce數量為10,使用 rand(), 隨機生成一個數  x % 10 , 這樣資料就會隨機進入 reduce 中,防止出現有的檔案過大或過小。

    4. 使用hadoop的archive將小檔案歸檔

    Hadoop Archive簡稱HAR,是一個高效地將小檔案放入HDFS塊中的檔案存檔工具,它能夠將多個小檔案打包成一個HAR檔案,這樣在減少namenode記憶體使用的同時,仍然允許對檔案進行透明的訪問。

    
    #用來控制歸檔是否可用
    
    set hive.archive.enabled= true;
    #通知Hive在建立歸檔時是否可以設定父目錄
    set hive.archive.har.parentdir.settable= true;
    #控制需要歸檔檔案的大小
    set har.partfile.size= 1099511627776;

    使用以下命令進行歸檔:
    ALTER  TABLE A  ARCHIVE  PARTITION(dt= '2021-05-07', hr= '12');

    對已歸檔的分割槽恢復為原檔案:
    ALTER  TABLE A UNARCHIVE  PARTITION(dt= '2021-05-07', hr= '12');

    注意:
    歸檔的分割槽可以檢視不能 insert overwrite,必須先 unarchive

    Hive 小檔案問題具體可檢視: 解決hive小檔案過多問題

    11. Hive最佳化有哪些

    1. 資料儲存及壓縮:

    針對hive中表的儲存格式通常有orc和parquet,壓縮格式一般使用snappy。相比與textfile格式表,orc佔有更少的儲存。因為hive底層使用MR計算架構,資料流是hdfs到磁碟再到hdfs,而且會有很多次,所以使用orc資料格式和snappy壓縮策略可以降低IO讀寫,還能降低網路傳輸量,這樣在一定程度上可以節省儲存,還能提升hql任務執行效率;

    2. 透過調參最佳化:

    並行執行,調節parallel引數;

    調節jvm引數,重用jvm;

    設定map、reduce的引數;開啟strict mode模式;

    關閉推測執行設定。

    3. 有效地減小資料集將大表拆分成子表;結合使用外部表和分割槽表。
    4. SQL最佳化
    • 大表對大表:儘量減少資料集,可以透過分割槽表,避免掃描全表或者全欄位;

    • 大表對小表:設定自動識別小表,將小表放入記憶體中去執行。

    Hive最佳化詳細剖析可檢視: Hive企業級效能最佳化

    Spark

    1. Spark 的執行流程?

    Spark執行流程

    具體執行流程如下:

    1. SparkContext 向資源管理器註冊並向資源管理器申請執行 Executor

    2. 資源管理器分配 Executor,然後資源管理器啟動 Executor

    3. Executor 傳送心跳至資源管理器

    4. SparkContext 構建 DAG 有向無環圖

    5. 將 DAG 分解成 Stage(TaskSet)

    6. 把 Stage 傳送給 TaskScheduler

    7. Executor 向 SparkContext 申請 Task

    8. TaskScheduler 將 Task 傳送給 Executor 執行

    9. 同時 SparkContext 將應用程式程式碼發放給 Executor

    10. Task 在 Executor 上執行,執行完畢釋放所有資源

    2. Spark 有哪些元件?

    1. master:管理叢集和節點,不參與計算。

    2. worker:計算節點,程式本身不參與計算,和 master 彙報。

    3. Driver:執行程式的 main 方法,建立 spark context 物件。

    4. spark context:控制整個 application 的生命週期,包括 dagsheduler 和 task scheduler 等元件。

    5. client:使用者提交程式的入口。

    3. Spark 中的 RDD 機制理解嗎?

    rdd 分散式彈性資料集,簡單的理解成一種資料結構,是 spark 框架上的通用貨幣。所有運算元都是基於 rdd 來執行的,不同的場景會有不同的 rdd 實現類,但是都可以進行互相轉換。rdd 執行過程中會形成 dag 圖,然後形成 lineage 保證容錯性等。從物理的角度來看 rdd 儲存的是 block 和 node 之間的對映。

    RDD 是 spark 提供的核心抽象,全稱為彈性分散式資料集。

    RDD 在邏輯上是一個 hdfs 檔案,在抽象上是一種元素集合,包含了資料。它是被分割槽的,分為多個分割槽,每個分割槽分佈在叢集中的不同結點上,從而讓 RDD 中的資料可以被並行操作(分散式資料集)

    比如有個 RDD 有 90W 資料,3 個 partition,則每個分割槽上有 30W 資料。RDD 通常透過 Hadoop 上的檔案,即 HDFS 或者 HIVE 表來建立,還可以透過應用程式中的集合來建立;RDD 最重要的特性就是容錯性,可以自動從節點失敗中恢復過來。即如果某個結點上的 RDD partition 因為節點故障,導致資料丟失,那麼 RDD 可以透過自己的資料來源重新計算該 partition。這一切對使用者都是透明的。

    RDD 的資料預設存放在記憶體中,但是當記憶體資源不足時,spark 會自動將 RDD 資料寫入磁碟。比如某結點記憶體只能處理 20W 資料,那麼這 20W 資料就會放入記憶體中計算,剩下 10W 放到磁碟中。RDD 的彈性體現在於 RDD 上自動進行記憶體和磁碟之間權衡和切換的機制。

    4. RDD 中 reduceBykey 與 groupByKey 哪個效能好,為什麼?

    reduceByKey:reduceByKey 會在結果傳送至 reducer 之前會對每個 mapper 在本地進行 merge,有點類似於在 MapReduce 中的 combiner。這樣做的好處在於,在 map 端進行一次 reduce 之後,資料量會大幅度減小,從而減小傳輸,保證 reduce 端能夠更快的進行結果計算。

    groupByKey:groupByKey 會對每一個 RDD 中的 value 值進行聚合形成一個序列(Iterator),此操作發生在 reduce 端,所以勢必會將所有的資料透過網路進行傳輸,造成不必要的浪費。同時如果資料量十分大,可能還會造成 OutOfMemoryError。

    所以在進行大量資料的 reduce 操作時候建議使用 reduceByKey。不僅可以提高速度,還可以防止使用 groupByKey 造成的記憶體溢位問題。

    5. 介紹一下 cogroup rdd 實現原理,你在什麼場景下用過這個 rdd?

    cogroup:對多個(2~4)RDD 中的 KV 元素,每個 RDD 中相同 key 中的元素分別聚合成一個集合。

    與 reduceByKey 不同的是:reduceByKey 針對 一個 RDD中相同的 key 進行合併。而 cogroup 針對 多個 RDD中相同的 key 的元素進行合併。

    cogroup 的函式實現:這個實現根據要進行合併的兩個 RDD 操作,生成一個 CoGroupedRDD 的例項,這個 RDD 的返回結果是把相同的 key 中兩個 RDD 分別進行合併操作,最後返回的 RDD 的 value 是一個 Pair 的例項,這個例項包含兩個 Iterable 的值,第一個值表示的是 RDD1 中相同 KEY 的值,第二個值表示的是 RDD2 中相同 key 的值。

    由於做 cogroup 的操作,需要透過 partitioner 進行重新分割槽的操作,因此,執行這個流程時,需要執行一次 shuffle 的操作(如果要進行合併的兩個 RDD 的都已經是 shuffle 後的 rdd,同時他們對應的 partitioner 相同時,就不需要執行 shuffle)。

    場景:表關聯查詢或者處理重複的 key。

    6. 如何區分 RDD 的寬窄依賴?

    窄依賴:父 RDD 的一個分割槽只會被子 RDD 的一個分割槽依賴;

    寬依賴:父 RDD 的一個分割槽會被子 RDD 的多個分割槽依賴(涉及到 shuffle)。

    7. 為什麼要設計寬窄依賴?

    1. 對於窄依賴
      窄依賴的多個分割槽可以平行計算;
      窄依賴的一個分割槽的資料如果丟失只需要重新計算對應的分割槽的資料就可以了。

    2. 對於寬依賴
      劃分 Stage(階段)的依據:對於寬依賴,必須等到上一階段計算完成才能計算下一階段。

    8. DAG 是什麼?

    DAG(Directed Acyclic Graph 有向無環圖)指的是資料轉換執行的過程,有方向,無閉環(其實就是 RDD 執行的流程);
    原始的 RDD 透過一系列的轉換操作就形成了 DAG 有向無環圖,任務執行時,可以按照 DAG 的描述,執行真正的計算(資料被操作的一個過程)。

    9. DAG 中為什麼要劃分 Stage?

    平行計算

    一個複雜的業務邏輯如果有 shuffle,那麼就意味著前面階段產生結果後,才能執行下一個階段,即下一個階段的計算要依賴上一個階段的資料。那麼我們按照 shuffle 進行劃分(也就是按照寬依賴就行劃分),就可以將一個 DAG 劃分成多個 Stage/階段,在同一個 Stage 中,會有多個運算元操作,可以形成一個 pipeline 流水線,流水線內的多個平行的分割槽可以並行執行。

    10. 如何劃分 DAG 的 stage?

    對於窄依賴,partition 的轉換處理在 stage 中完成計算,不劃分(將窄依賴儘量放在在同一個 stage 中,可以實現流水線計算)。

    對於寬依賴,由於有 shuffle 的存在,只能在父 RDD 處理完成後,才能開始接下來的計算,也就是說需要要劃分 stage。

    11. DAG 劃分為 Stage 的演算法瞭解嗎?

    核心演算法:回溯演算法

    從後往前回溯/反向解析,遇到窄依賴加入本 Stage,遇見寬依賴進行 Stage 切分。

    Spark 核心會從觸發 Action 操作的那個 RDD 開始 從後往前推,首先會為最後一個 RDD 建立一個 Stage,然後繼續倒推,如果發現對某個 RDD 是寬依賴,那麼就會將寬依賴的那個 RDD 建立一個新的 Stage,那個 RDD 就是新的 Stage 的最後一個 RDD。然後依次類推,繼續倒推,根據窄依賴或者寬依賴進行 Stage 的劃分,直到所有的 RDD 全部遍歷完成為止。

    具體劃分演算法請參考:AMP 實驗室發表的論文
    《Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing》

    12. 對於 Spark 中的資料傾斜問題你有什麼好的方案?

    1. 前提是定位資料傾斜,是 OOM 了,還是任務執行緩慢,看日誌,看 WebUI

    2. 解決方法,有多個方面:

    • 避免不必要的 shuffle,如使用廣播小表的方式,將 reduce-side-join 提升為 map-side-join
    • 分拆發生資料傾斜的記錄,分成幾個部分進行,然後合併 join 後的結果
    • 改變並行度,可能並行度太少了,導致個別 task 資料壓力大
    • 兩階段聚合,先區域性聚合,再全域性聚合
    • 自定義 paritioner,分散 key 的分佈,使其更加均勻

    13. Spark 中的 OOM 問題?

    1. map 型別的運算元執行中記憶體溢位如 flatMap,mapPatitions
    • 原因:map 端過程產生大量物件導致記憶體溢位:這種溢位的原因是在單個 map 中產生了大量的物件導致的針對這種問題。
    1. 解決方案:
    • 增加堆內記憶體。
    • 在不增加記憶體的情況下,可以減少每個 Task 處理資料量,使每個 Task 產生大量的物件時,Executor 的記憶體也能夠裝得下。具體做法可以在會產生大量物件的 map 操作之前呼叫 repartition 方法,分割槽成更小的塊傳入 map。
    1. shuffle 後記憶體溢位如 join,reduceByKey,repartition。
    • shuffle 記憶體溢位的情況可以說都是 shuffle 後,單個檔案過大導致的。在 shuffle 的使用,需要傳入一個 partitioner,大部分 Spark 中的 shuffle 操作,預設的 partitioner 都是 HashPatitioner,預設值是父 RDD 中最大的分割槽數.這個引數 spark.default.parallelism 只對 HashPartitioner 有效.如果是別的 partitioner 導致的 shuffle 記憶體溢位就需要重寫 partitioner 程式碼了.
    1. driver 記憶體溢位
    • 使用者在 Dirver 埠生成大物件,比如建立了一個大的集合資料結構。解決方案:將大物件轉換成 Executor 端載入,比如呼叫 sc.textfile 或者評估大物件佔用的記憶體,增加 dirver 端的記憶體

    • 從 Executor 端收集資料(collect)回 Dirver 端,建議將 driver 端對 collect 回來的資料所作的操作,轉換成 executor 端 rdd 操作。

    14. Spark 中資料的位置是被誰管理的?

    每個資料分片都對應具體物理位置,資料的位置是被 blockManager管理,無論資料是在磁碟,記憶體還是 tacyan,都是由 blockManager 管理。

    15. Spaek 程式執行,有時候預設為什麼會產生很多 task,怎麼修改預設 task 執行個數?

    1. 輸入資料有很多 task,尤其是有很多小檔案的時候,有多少個輸入 block 就會有多少個 task 啟動;

    2. spark 中有 partition 的概念,每個 partition 都會對應一個 task,task 越多,在處理大規模資料的時候,就會越有效率。不過 task 並不是越多越好,如果平時測試,或者資料量沒有那麼大,則沒有必要 task 數量太多。

    3. 引數可以透過 spark_home/conf/spark-default.conf 配置檔案設定:

    針對 spark sql 的 task 數量: spark.sql.shuffle.partitions=50

    非 spark sql 程式設定生效: spark.default.parallelism=10

    16. 介紹一下 join 操作最佳化經驗?

    這道題常考,這裡只是給大家一個思路,簡單說下!面試之前還需做更多準備。

    join 其實常見的就分為兩類: map-side join 和  reduce-side join

    當大表和小表 join 時,用 map-side join 能顯著提高效率。

    將多份資料進行關聯是資料處理過程中非常普遍的用法,不過在分散式計算系統中,這個問題往往會變的非常麻煩,因為框架提供的 join 操作一般會將所有資料根據 key 傳送到所有的 reduce 分割槽中去,也就是 shuffle 的過程。造成大量的網路以及磁碟 IO 消耗,執行效率極其低下,這個過程一般被稱為 reduce-side-join。

    如果其中有張表較小的話,我們則可以自己實現在 map 端實現資料關聯,跳過大量資料進行 shuffle 的過程,執行時間得到大量縮短,根據不同資料可能會有幾倍到數十倍的效能提升。

    在大資料量的情況下,join 是一中非常昂貴的操作,需要在 join 之前應儘可能的先縮小資料量。

    對於縮小資料量,有以下幾條建議

    1. 若兩個 RDD 都有重複的 key,join 操作會使得資料量會急劇的擴大。所有,最好先使用 distinct 或者 combineByKey 操作來減少 key 空間或者用 cogroup 來處理重複的 key,而不是產生所有的交叉結果。在 combine 時,進行機智的分割槽,可以避免第二次 shuffle。

    2. 如果只在一個 RDD 出現,那你將在無意中丟失你的資料。所以使用外連線會更加安全,這樣你就能確保左邊的 RDD 或者右邊的 RDD 的資料完整性,在 join 之後再過濾資料。

    3. 如果我們容易得到 RDD 的可以的有用的子集合,那麼我們可以先用 filter 或者 reduce,如何在再用 join。

    17. Spark 與 MapReduce 的 Shuffle 的區別?

    1. 相同點:都是將 mapper(Spark 裡是 ShuffleMapTask)的輸出進行 partition,不同的 partition 送到不同的 reducer(Spark 裡 reducer 可能是下一個 stage 裡的 ShuffleMapTask,也可能是 ResultTask)

    2. 不同點:

    • MapReduce 預設是排序的,spark 預設不排序,除非使用 sortByKey 運算元。

    • MapReduce 可以劃分成 split,map()、spill、merge、shuffle、sort、reduce()等階段,spark 沒有明顯的階段劃分,只有不同的 stage 和運算元操作。

    • MR 落盤,Spark 不落盤,spark 可以解決 mr 落盤導致效率低下的問題。

    18. Spark SQL 執行的流程?

    這個問題如果深挖還挺複雜的,這裡簡單介紹下總體流程:

    1. parser:基於 antlr 框架對 sql 解析,生成抽象語法樹。

    2. 變數替換:透過正規表示式找出符合規則的字串,替換成系統快取環境的變數

    SQLConf 中的 spark.sql.variable.substitute,預設是可用的;參考 SparkSqlParser

    1. parser:將 antlr 的 tree 轉成 spark catalyst 的 LogicPlan,也就是 未解析的邏輯計劃;詳細參考 AstBuildParseDriver

    2. analyzer:透過分析器,結合 catalog,把 logical plan 和實際的資料繫結起來,將 未解析的邏輯計劃 生成 邏輯計劃;詳細參考 QureyExecution

    3. 快取替換:透過 CacheManager,替換有相同結果的 logical plan(邏輯計劃)

    4. logical plan 最佳化,基於規則的最佳化;最佳化規則參考 Optimizer,最佳化執行器 RuleExecutor

    5. 生成 spark plan,也就是物理計劃;參考 QueryPlannerSparkStrategies

    6. spark plan 準備階段

    7. 構造 RDD 執行,涉及 spark 的 wholeStageCodegenExec 機制,基於 janino 框架生成 java 程式碼並編譯

    19. Spark SQL 是如何將資料寫到 Hive 表的?

    • 方式一:是利用 Spark RDD 的 API 將資料寫入 hdfs 形成 hdfs 檔案,之後再將 hdfs 檔案和 hive 表做載入對映。

    • 方式二:利用 Spark SQL 將獲取的資料 RDD 轉換成 DataFrame,再將 DataFrame 寫成快取表,最後利用 Spark SQL 直接插入 hive 表中。而對於利用 Spark SQL 寫 hive 表官方有兩種常見的 API,第一種是利用 JavaBean 做對映,第二種是利用 StructType 建立 Schema 做對映。

    20. 通常來說,Spark 與 MapReduce 相比,Spark 執行效率更高。請說明效率更高來源於 Spark 內建的哪些機制?

    1. 基於記憶體計算,減少低效的磁碟互動;
    2. 高效的排程演算法,基於 DAG;
    3. 容錯機制 Linage。

    重點部分就是 DAG 和 Lingae

    21. Hadoop 和 Spark 的相同點和不同點?

    Hadoop 底層使用 MapReduce 計算架構,只有 map 和 reduce 兩種操作,表達能力比較欠缺,而且在 MR 過程中會重複的讀寫 hdfs,造成大量的磁碟 io 讀寫操作,所以適合高時延環境下批處理計算的應用;

    Spark 是基於記憶體的分散式計算架構,提供更加豐富的資料集操作型別,主要分成轉化操作和行動操作,包括 map、reduce、filter、flatmap、groupbykey、reducebykey、union 和 join 等,資料分析更加快速,所以適合低時延環境下計算的應用;

    spark 與 hadoop 最大的區別在於迭代式計算模型。基於 mapreduce 框架的 Hadoop 主要分為 map 和 reduce 兩個階段,兩個階段完了就結束了,所以在一個 job 裡面能做的處理很有限;spark 計算模型是基於記憶體的迭代式計算模型,可以分為 n 個階段,根據使用者編寫的 RDD 運算元和程式,在處理完一個階段後可以繼續往下處理很多個階段,而不只是兩個階段。所以 spark 相較於 mapreduce,計算模型更加靈活,可以提供更強大的功能。

    但是 spark 也有劣勢,由於 spark 基於記憶體進行計算,雖然開發容易,但是真正面對大資料的時候,在沒有進行調優的情況下,可能會出現各種各樣的問題,比如 OOM 記憶體溢位等情況,導致 spark 程式可能無法執行起來,而 mapreduce 雖然執行緩慢,但是至少可以慢慢執行完。

    22. Hadoop 和 Spark 使用場景?

    Hadoop/MapReduce 和 Spark 最適合的都是做離線型的資料分析,但 Hadoop 特別適合是單次分析的資料量“很大”的情景,而 Spark 則適用於資料量不是很大的情景。

    1. 一般情況下,對於中小網際網路和企業級的大資料應用而言,單次分析的數量都不會“很大”,因此可以優先考慮使用 Spark。

    2. 業務通常認為 Spark 更適用於機器學習之類的“迭代式”應用,80GB 的壓縮資料(解壓後超過 200GB),10 個節點的叢集規模,跑類似“sum+group-by”的應用,MapReduce 花了 5 分鐘,而 spark 只需要 2 分鐘。

    23. Spark 如何保證當機迅速恢復?

    1. 適當增加 spark standby master

    2. 編寫 shell 指令碼,定期檢測 master 狀態,出現當機後對 master 進行重啟操作

    24. RDD 持久化原理?

    spark 非常重要的一個功能特性就是可以將 RDD 持久化在記憶體中。

    呼叫 cache()和 persist()方法即可。cache()和 persist()的區別在於,cache()是 persist()的一種簡化方式,cache()的底層就是呼叫 persist()的無參版本 persist(MEMORY_ONLY),將資料持久化到記憶體中。

    如果需要從記憶體中清除快取,可以使用 unpersist()方法。RDD 持久化是可以手動選擇不同的策略的。在呼叫 persist()時傳入對應的 StorageLevel 即可。

    25. Checkpoint 檢查點機制?

    應用場景:當 spark 應用程式特別複雜,從初始的 RDD 開始到最後整個應用程式完成有很多的步驟,而且整個應用執行時間特別長,這種情況下就比較適合使用 checkpoint 功能。

    原因:對於特別複雜的 Spark 應用,會出現某個反覆使用的 RDD,即使之前持久化過但由於節點的故障導致資料丟失了,沒有容錯機制,所以需要重新計算一次資料。

    Checkpoint 首先會呼叫 SparkContext 的 setCheckPointDIR()方法,設定一個容錯的檔案系統的目錄,比如說 HDFS;然後對 RDD 呼叫 checkpoint()方法。之後在 RDD 所處的 job 執行結束之後,會啟動一個單獨的 job,來將 checkpoint 過的 RDD 資料寫入之前設定的檔案系統,進行高可用、容錯的類持久化操作。

    檢查點機制是我們在 spark streaming 中用來保障容錯性的主要機制,它可以使 spark streaming 階段性的把應用資料儲存到諸如 HDFS 等可靠儲存系統中,以供恢復時使用。具體來說基於以下兩個目的服務:

    1. 控制發生失敗時需要重算的狀態數。Spark streaming 可以透過轉化圖的譜系圖來重算狀態,檢查點機制則可以控制需要在轉化圖中回溯多遠。

    2. 提供驅動器程式容錯。如果流計算應用中的驅動器程式崩潰了,你可以重啟驅動器程式並讓驅動器程式從檢查點恢復,這樣 spark streaming 就可以讀取之前執行的程式處理資料的進度,並從那裡繼續。

    26. Checkpoint 和持久化機制的區別?

    最主要的區別在於持久化只是將資料儲存在 BlockManager 中,但是 RDD 的 lineage(血緣關係,依賴關係)是不變的。但是 checkpoint 執行完之後,rdd 已經沒有之前所謂的依賴 rdd 了,而只有一個強行為其設定的 checkpointRDD,checkpoint 之後 rdd 的 lineage 就改變了。

    持久化的資料丟失的可能性更大,因為節點的故障會導致磁碟、記憶體的資料丟失。但是 checkpoint 的資料通常是儲存在高可用的檔案系統中,比如 HDFS 中,所以資料丟失可能性比較低

    27. Spark Streaming 以及基本工作原理?

    Spark streaming 是 spark core API 的一種擴充套件,可以用於進行大規模、高吞吐量、容錯的實時資料流的處理。

    它支援從多種資料來源讀取資料,比如 Kafka、Flume、Twitter 和 TCP Socket,並且能夠使用運算元比如 map、reduce、join 和 window 等來處理資料,處理後的資料可以儲存到檔案系統、資料庫等儲存中。

    Spark streaming 內部的基本工作原理是:接受實時輸入資料流,然後將資料拆分成 batch,比如每收集一秒的資料封裝成一個 batch,然後將每個 batch 交給 spark 的計算引擎進行處理,最後會生產處一個結果資料流,其中的資料也是一個一個的 batch 組成的。

    28. DStream 以及基本工作原理?

    DStream 是 spark streaming 提供的一種高階抽象,代表了一個持續不斷的資料流。

    DStream 可以透過輸入資料來源來建立,比如 Kafka、flume 等,也可以透過其他 DStream 的高階函式來建立,比如 map、reduce、join 和 window 等。

    DStream 內部其實不斷產生 RDD,每個 RDD 包含了一個時間段的資料。

    Spark streaming 一定是有一個輸入的 DStream 接收資料,按照時間劃分成一個一個的 batch,並轉化為一個 RDD,RDD 的資料是分散在各個子節點的 partition 中。

    29. Spark Streaming 整合 Kafka 的兩種模式?

    1. receiver 方式:將資料拉取到 executor 中做操作,若資料量大,記憶體儲存不下,可以透過 WAL,設定了本地儲存,保證資料不丟失,然後使用 Kafka 高階 API 透過 zk 來維護偏移量,保證消費資料。receiver 消費的資料偏移量是在 zk 獲取的, 此方式效率低,容易出現資料丟失
    • receiver 方式的容錯性:在預設的配置下,這種方式可能會因為底層的失敗而丟失資料。如果要啟用高可靠機制,讓資料零丟失,就必須啟用 Spark Streaming 的預寫日誌機制(Write Ahead Log,WAL)。該機制會同步地將接收到的 Kafka 資料寫入分散式檔案系統(比如 HDFS)上的預寫日誌中。所以,即使底層節點出現了失敗,也可以使用預寫日誌中的資料進行恢復。

    • Kafka 中的 topic 的 partition,與 Spark 中的 RDD 的 partition 是沒有關係的。在 1、KafkaUtils.createStream()中,提高 partition 的數量,只會增加 Receiver 方式中讀取 partition 的執行緒的數量。不會增加 Spark 處理資料的並行度。可以建立多個 Kafka 輸入 DStream,使用不同的 consumer group 和 topic,來透過多個 receiver 並行接收資料。

    1. 基於 Direct 方式使用 Kafka 底層 Api,其消費者直接連線 kafka 的分割槽上,因為 createDirectStream 建立的 DirectKafkaInputDStream 每個 batch 所對應的 RDD 的分割槽與 kafka 分割槽一一對應,但是需要自己維護偏移量,即用即取,不會給記憶體造成太大的壓力,效率高。
    • 優點:簡化並行讀取:如果要讀取多個 partition,不需要建立多個輸入 DStream 然後對它們進行 union 操作。Spark 會建立跟 Kafka partition 一樣多的 RDD partition,並且會並行從 Kafka 中讀取資料。所以在 Kafka partition 和 RDD partition 之間,有一個一對一的對映關係。

    • 高效能:如果要保證零資料丟失,在基於 receiver 的方式中,需要開啟 WAL 機制。這種方式其實效率低下,因為資料實際上被複制了兩份,Kafka 自己本身就有高可靠的機制,會對資料複製一份,而這裡又會複製一份到 WAL 中。而基於 direct 的方式,不依賴 Receiver,不需要開啟 WAL 機制,只要 Kafka 中作了資料的複製,那麼就可以透過 Kafka 的副本進行恢復。

    1. receiver 與和 direct 的比較:
    • 基於 receiver 的方式,是使用 Kafka 的高階 API 來在 ZooKeeper 中儲存消費過的 offset 的。這是消費 Kafka 資料的傳統方式。這種方式配合著 WAL 機制可以保證資料零丟失的高可靠性,但是卻無法保證資料被處理一次且僅一次,可能會處理兩次。因為 Spark 和 ZooKeeper 之間可能是不同步的。

    • 基於 direct 的方式,使用 Kafka 的低階 API,Spark Streaming 自己就負責追蹤消費的 offset,並儲存在 checkpoint 中。Spark 自己一定是同步的,因此可以保證資料是消費一次且僅消費一次。

    • Receiver 方式是透過 zookeeper 來連線 kafka 佇列,Direct 方式是直接連線到 kafka 的節點上獲取資料。

    30. Spark 主備切換機制原理知道嗎?

    Master 實際上可以配置兩個,Spark 原生的 standalone 模式是支援 Master 主備切換的。當 Active Master 節點掛掉以後,我們可以將 Standby Master 切換為 Active Master。

    Spark Master 主備切換可以基於兩種機制,一種是基於檔案系統的,一種是基於 ZooKeeper 的。

    基於檔案系統的主備切換機制,需要在 Active Master 掛掉之後手動切換到 Standby Master 上;

    而基於 Zookeeper 的主備切換機制,可以實現自動切換 Master。

    31. Spark 解決了 Hadoop 的哪些問題?

    1. MR:抽象層次低,需要使用手工程式碼來完成程式編寫,使用上難以上手;

      Spark:Spark 採用 RDD 計算模型,簡單容易上手。

    2. MR:只提供 map 和 reduce 兩個操作,表達能力欠缺;

      Spark:Spark 採用更加豐富的運算元模型,包括 map、flatmap、groupbykey、reducebykey 等;

    3. MR:一個 job 只能包含 map 和 reduce 兩個階段,複雜的任務需要包含很多個 job,這些 job 之間的管理以來需要開發者自己進行管理;

      Spark:Spark 中一個 job 可以包含多個轉換操作,在排程時可以生成多個 stage,而且如果多個 map 操作的分割槽不變,是可以放在同一個 task 裡面去執行;

    4. MR:中間結果存放在 hdfs 中;

      Spark:Spark 的中間結果一般存在記憶體中,只有當記憶體不夠了,才會存入本地磁碟,而不是 hdfs;

    5. MR:只有等到所有的 map task 執行完畢後才能執行 reduce task;

      Spark:Spark 中分割槽相同的轉換構成流水線在一個 task 中執行,分割槽不同的需要進行 shuffle 操作,被劃分成不同的 stage 需要等待前面的 stage 執行完才能執行。

    6. MR:只適合 batch 批處理,時延高,對於互動式處理和實時處理支援不夠;

      Spark:Spark streaming 可以將流拆成時間間隔的 batch 進行處理,實時計算。

    32. 資料傾斜的產生和解決辦法?

    資料傾斜以為著某一個或者某幾個 partition 的資料特別大,導致這幾個 partition 上的計算需要耗費相當長的時間。

    在 spark 中同一個應用程式劃分成多個 stage,這些 stage 之間是序列執行的,而一個 stage 裡面的多個 task 是可以並行執行,task 數目由 partition 數目決定,如果一個 partition 的數目特別大,那麼導致這個 task 執行時間很長,導致接下來的 stage 無法執行,從而導致整個 job 執行變慢。

    避免資料傾斜,一般是要選用合適的 key,或者自己定義相關的 partitioner,透過加鹽或者雜湊值來拆分這些 key,從而將這些資料分散到不同的 partition 去執行。

    如下運算元會導致 shuffle 操作,是導致資料傾斜可能發生的關鍵點所在:groupByKey;reduceByKey;aggregaByKey;join;cogroup;

    33. 你用 Spark Sql 處理的時候, 處理過程中用的 DataFrame 還是直接寫的 Sql?為什麼?

    這個問題的宗旨是問你 spark sql 中 dataframe 和 sql 的區別,從執行原理、操作方便程度和自定義程度來分析 這個問題。

    34. Spark Master HA 主從切換過程不會影響到叢集已有作業的執行,為什麼?

    不會的。

    因為程式在執行之前,已經申請過資源了,driver 和 Executors 通訊,不需要和 master 進行通訊的。

    35. Spark Master 使用 Zookeeper 進行 HA,有哪些源資料儲存到 Zookeeper 裡面?

    spark 透過這個引數 spark.deploy.zookeeper.dir 指定 master 後設資料在 zookeeper 中儲存的位置,包括 Worker,Driver 和 Application 以及 Executors。standby 節點要從 zk 中,獲得後設資料資訊,恢復叢集執行狀態,才能對外繼續提供服務,作業提交資源申請等,在恢復前是不能接受請求的。

    注:Master 切換需要注意 2 點:
    1、在 Master 切換的過程中,所有的已經在執行的程式皆正常執行!因為 Spark Application 在執行前就已經透過 Cluster Manager 獲得了計算資源,所以在執行時 Job 本身的 排程和處理和 Master 是沒有任何關係。
    2、在 Master 的切換過程中唯一的影響是不能提交新的 Job:一方面不能夠提交新的應用程式給叢集, 因為只有 Active Master 才能接受新的程式的提交請求;另外一方面,已經執行的程式中也不能夠因 Action 操作觸發新的 Job 的提交請求。

    36. 如何實現Spark Streaming讀取Flume中的資料?

    可以這樣說:

    • 前期經過技術調研,檢視官網相關資料,發現sparkStreaming整合flume有2種模式,一種是拉模式,一種是推模式,然後在簡單的聊聊這2種模式的特點,以及如何部署實現,需要做哪些事情,最後對比兩種模式的特點,選擇那種模式更好。

    • 推模式:Flume將資料Push推給Spark Streaming

    • 拉模式:Spark Streaming從flume 中Poll拉取資料

    37. 在實際開發的時候是如何保證資料不丟失的?

    可以這樣說:

    • flume那邊採用的channel是將資料落地到磁碟中,保證資料來源端安全性(可以在補充一下,flume在這裡的channel可以設定為memory記憶體中,提高資料接收處理的效率,但是由於資料在記憶體中,安全機制保證不了,故選擇channel為磁碟儲存。整個流程執行有一點的延遲性)

    • sparkStreaming透過拉模式整合的時候,使用了FlumeUtils這樣一個類,該類是需要依賴一個額外的jar包(spark-streaming-flume_2.10)

    • 要想保證資料不丟失,資料的準確性,可以在構建StreamingConext的時候,利用StreamingContext.getOrCreate(checkpoint, creatingFunc: () => StreamingContext)來建立一個StreamingContext,使用StreamingContext.getOrCreate來建立StreamingContext物件,傳入的第一個引數是checkpoint的存放目錄,第二引數是生成StreamingContext物件的使用者自定義函式。如果checkpoint的存放目錄存在,則從這個目錄中生成StreamingContext物件;如果不存在,才會呼叫第二個函式來生成新的StreamingContext物件。在creatingFunc函式中,除了生成一個新的StreamingContext操作,還需要完成各種操作,然後呼叫ssc.checkpoint(checkpointDirectory)來初始化checkpoint功能,最後再返回StreamingContext物件。

      這樣,在StreamingContext.getOrCreate之後,就可以直接呼叫start()函式來啟動(或者是從中斷點繼續執行)流式應用了。如果有其他在啟動或繼續執行都要做的工作,可以在start()呼叫前執行。

    38. RDD有哪些缺陷?

    1. 不支援細粒度的寫和更新操作,Spark寫資料是粗粒度的,所謂粗粒度,就是批次寫入資料,目的是為了提高效率。但是Spark讀資料是細粒度的,也就是說可以一條條的讀。

    2. 不支援增量迭代計算,如果對Flink熟悉,可以說下Flink支援增量迭代計算。

    Kafka

    1. 為什麼要使用 kafka?

    1. 緩衝和削峰:上游資料時有突發流量,下游可能扛不住,或者下游沒有足夠多的機器來保證冗餘,kafka在中間可以起到一個緩衝的作用,把訊息暫存在kafka中,下游服務就可以按照自己的節奏進行慢慢處理。

    2. 解耦和擴充套件性:專案開始的時候,並不能確定具體需求。訊息佇列可以作為一個介面層,解耦重要的業務流程。只需要遵守約定,針對資料程式設計即可獲取擴充套件能力。

    3. 冗餘:可以採用一對多的方式,一個生產者釋出訊息,可以被多個訂閱topic的服務消費到,供多個毫無關聯的業務使用。

    4. 健壯性:訊息佇列可以堆積請求,所以消費端業務即使短時間死掉,也不會影響主要業務的正常進行。

    5. 非同步通訊:很多時候,使用者不想也不需要立即處理訊息。訊息佇列提供了非同步處理機制,允許使用者把一個訊息放入佇列,但並不立即處理它。想向佇列中放入多少訊息就放多少,然後在需要的時候再去處理它們。

    2. Kafka消費過的訊息如何再消費?

    kafka消費訊息的offset是定義在zookeeper中的, 如果想重複消費kafka的訊息,可以在redis中自己記錄offset的checkpoint點(n個),當想重複消費訊息時,透過讀取redis中的checkpoint點進行zookeeper的offset重設,這樣就可以達到重複消費訊息的目的了

    3. kafka的資料是放在磁碟上還是記憶體上,為什麼速度會快?

    kafka使用的是磁碟儲存。

    速度快是因為:

    1. 順序寫入:因為硬碟是機械結構,每次讀寫都會定址->寫入,其中定址是一個“機械動作”,它是耗時的。所以硬碟 “討厭”隨機I/O, 喜歡順序I/O。為了提高讀寫硬碟的速度,Kafka就是使用順序I/O。
    2. Memory Mapped Files(記憶體對映檔案):64位作業系統中一般可以表示20G的資料檔案,它的工作原理是直接利用作業系統的Page來實現檔案到實體記憶體的直接對映。完成對映之後你對實體記憶體的操作會被同步到硬碟上。
    3. Kafka高效檔案儲存設計:Kafka把topic中一個parition大檔案分成多個小檔案段,透過多個小檔案段,就容易定期清除或刪除已經消費完檔案,減少磁碟佔用。透過索引資訊可以快速定位 message和確定response的 大     小。透過index後設資料全部對映到memory(記憶體對映檔案), 可以避免segment file的IO磁碟操作。透過索引檔案稀疏儲存,可以大幅降低index檔案後設資料佔用空間大小。

    1. Kafka解決查詢效率的手段之一是將資料檔案分段,比如有100條Message,它們的offset是從0到99。假設將資料檔案分成5段,第一段為0-19,第二段為20-39,以此類推,每段放在一個單獨的資料檔案裡面,資料檔案以該段中 小的offset命名。這樣在查詢指定offset的 Message的時候,用二分查詢就可以定位到該Message在哪個段中。
    2. 為資料檔案建 索引資料檔案分段 使得可以在一個較小的資料檔案中查詢對應offset的Message 了,但是這依然需要順序掃描才能找到對應offset的Message。為了進一步提高查詢的效率,Kafka為每個分段後的資料檔案建立了索引檔案,檔名與資料檔案的名字是一樣的,只是副檔名為.index。

    4. Kafka資料怎麼保障不丟失?

    分三個點說,一個是生產者端,一個消費者端,一個broker端。

    1. 生產者資料的不丟失

    kafka的ack機制:在kafka傳送資料的時候,每次傳送訊息都會有一個確認反饋機制,確保訊息正常的能夠被收到,其中狀態有0,1,-1。

    如果是同步模式:
    ack設定為0,風險很大,一般不建議設定為0。即使設定為1,也會隨著leader當機丟失資料。所以如果要嚴格保證生產端資料不丟失,可設定為-1。

    如果是非同步模式:
    也會考慮ack的狀態,除此之外,非同步模式下的有個buffer,透過buffer來進行控制資料的傳送,有兩個值來進行控制,時間閾值與訊息的數量閾值,如果buffer滿了資料還沒有傳送出去,有個選項是配置是否立即清空buffer。可以設定為-1,永久阻塞,也就資料不再生產。非同步模式下,即使設定為-1。也可能因為程式設計師的不科學操作,運算元據丟失,比如kill -9,但這是特別的例外情況。

    注:
    ack=0:producer不等待broker同步完成的確認,繼續傳送下一條(批)資訊。
    ack=1(預設):producer要等待leader成功收到資料並得到確認,才傳送下一條message。
    ack=-1:producer得到follwer確認,才傳送下一條資料。

    1. 消費者資料的不丟失

    透過offset commit 來保證資料的不丟失,kafka自己記錄了每次消費的offset數值,下次繼續消費的時候,會接著上次的offset進行消費。

    而offset的資訊在kafka0.8版本之前儲存在zookeeper中,在0.8版本之後儲存到topic中,即使消費者在執行過程中掛掉了,再次啟動的時候會找到offset的值,找到之前消費訊息的位置,接著消費,由於 offset 的資訊寫入的時候並不是每條訊息消費完成後都寫入的,所以這種情況有可能會造成重複消費,但是不會丟失訊息。

    唯一例外的情況是,我們在程式中給原本做不同功能的兩個consumer組設定 KafkaSpoutConfig.bulider.setGroupid的時候設定成了一樣的groupid,這種情況會導致這兩個組共享同一份資料,就會產生組A消費partition1,partition2中的訊息,組B消費partition3的訊息,這樣每個組消費的訊息都會丟失,都是不完整的。為了保證每個組都獨享一份訊息資料,groupid一定不要重複才行。

    1. kafka叢集中的broker的資料不丟失

    每個broker中的partition我們一般都會設定有replication(副本)的個數,生產者寫入的時候首先根據分發策略(有partition按partition,有key按key,都沒有輪詢)寫入到leader中,follower(副本)再跟leader同步資料,這樣有了備份,也可以保證訊息資料的不丟失。

    5. 採集資料為什麼選擇kafka?

    採集層 主要可以使用Flume, Kafka等技術。

    Flume:Flume 是管道流方式,提供了很多的預設實現,讓使用者透過引數部署,及擴充套件API.

    Kafka:Kafka是一個可持久化的分散式的訊息佇列。Kafka 是一個非常通用的系統。你可以有許多生產者和很多的消費者共享多個主題Topics。

    相比之下,Flume是一個專用工具被設計為旨在往HDFS,HBase傳送資料。它對HDFS有特殊的最佳化,並且整合了Hadoop的安全特性。

    所以,Cloudera 建議如果資料被多個系統消費的話,使用kafka;如果資料被設計給Hadoop使用,使用Flume。

    6. kafka 重啟是否會導致資料丟失?

    1. kafka是將資料寫到磁碟的,一般資料不會丟失。
    2. 但是在重啟kafka過程中,如果有消費者消費訊息,那麼kafka如果來不及提交offset,可能會造成資料的不準確(丟失或者重複消費)。

    7. kafka 當機瞭如何解決?

    1. 先考慮業務是否受到影響

    kafka 當機了,首先我們考慮的問題應該是所提供的服務是否因為當機的機器而受到影響,如果服務提供沒問題,如果實現做好了叢集的容災機制,那麼這塊就不用擔心了。

    1. 節點排錯與恢復

    想要恢復叢集的節點,主要的步驟就是透過日誌分析來檢視節點當機的原因,從而解決,重新恢復節點。

    8. 為什麼Kafka不支援讀寫分離?

    在 Kafka 中,生產者寫入訊息、消費者讀取訊息的操作都是與 leader 副本進行互動的,從 而實現的是一種 主寫主讀的生產消費模型。Kafka 並不支援 主寫從讀,因為主寫從讀有 2 個很明顯的缺點:

    1. 資料一致性問題:資料從主節點轉到從節點必然會有一個延時的時間視窗,這個時間 視窗會導致主從節點之間的資料不一致。某一時刻,在主節點和從節點中 A 資料的值都為 X, 之後將主節點中 A 的值修改為 Y,那麼在這個變更通知到從節點之前,應用讀取從節點中的 A 資料的值並不為最新的 Y,由此便產生了資料不一致的問題。

    2. 延時問題:類似 Redis 這種元件,資料從寫入主節點到同步至從節點中的過程需要經歷 網路→主節點記憶體→網路→從節點記憶體 這幾個階段,整個過程會耗費一定的時間。而在 Kafka 中,主從同步會比 Redis 更加耗時,它需要經歷 網路→主節點記憶體→主節點磁碟→網路→從節 點記憶體→從節點磁碟 這幾個階段。對延時敏感的應用而言,主寫從讀的功能並不太適用。

    而kafka的 主寫主讀的優點就很多了:

    1. 可以簡化程式碼的實現邏輯,減少出錯的可能;
    2. 將負載粒度細化均攤,與主寫從讀相比,不僅負載效能更好,而且對使用者可控;
    3. 沒有延時的影響;
    4. 在副本穩定的情況下,不會出現資料不一致的情況。

    9. kafka資料分割槽和消費者的關係?

    每個分割槽只能由同一個消費組內的一個消費者(consumer)來消費,可以由不同的消費組的消費者來消費,同組的消費者則起到併發的效果。

    10. kafka的資料offset讀取流程

    1. 連線ZK叢集,從ZK中拿到對應topic的partition資訊和partition的Leader的相關資訊

    2. 連線到對應Leader對應的broker

    3. consumer將⾃自⼰己儲存的offset傳送給Leader

    4. Leader根據offset等資訊定位到segment(索引⽂檔案和⽇日誌⽂檔案)

    5. 根據索引⽂檔案中的內容,定位到⽇日誌⽂檔案中該偏移量量對應的開始位置讀取相應⻓長度的資料並返回給consumer

    11. kafka內部如何保證順序,結合外部元件如何保證消費者的順序?

    kafka只能保證partition內是有序的,但是partition間的有序是沒辦法的。愛奇藝的搜尋架構,是從業務上把需要有序的打到同⼀個partition。

    12. Kafka訊息資料積壓,Kafka消費能力不足怎麼處理?

    1. 如果是Kafka消費能力不足,則可以考慮增加Topic的分割槽數,並且同時提升消費組的消費者數量,消費者數=分割槽數。(兩者缺一不可)

    2. 如果是下游的資料處理不及時:提高每批次拉取的數量。批次拉取資料過少(拉取資料/處理時間<生產速度),使處理的資料小於生產的資料,也會造成資料積壓。

    13.  Kafka單條日誌傳輸大小

    kafka對於訊息體的大小預設為單條最大值是1M但是在我們應用場景中, 常常會出現一條訊息大於1M,如果不對kafka進行配置。則會出現生產者無法將訊息推送到kafka或消費者無法去消費kafka裡面的資料, 這時我們就要對kafka進行以下配置:server.properties

    replica.fetch.max.bytes: 1048576  broker可複製的訊息的最大位元組數, 預設為1M
    
    message.max.bytes: 1000012   kafka 會接收單個訊息size的最大限制, 預設為1M左右

    注意:message.max.bytes必須小於等於replica.fetch.max.bytes,否則就會導致replica之間資料同步失敗。

    Hbase

    1. Hbase是怎麼寫資料的?

    Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個StoreFile,直至增長到一定閾值 -> 觸發Compact合併操作 -> 多個StoreFile合併成一個StoreFile,同時進行版本合併和資料刪除 -> 當StoreFiles Compact後,逐步形成越來越大的StoreFile -> 單個StoreFile大小超過一定閾值後(預設10G),觸發Split操作,把當前Region Split成2個Region,Region會下線,新Split出的2個孩子Region會被HMaster分配到相應的HRegionServer 上,使得原先1個Region的壓力得以分流到2個Region上

    由此過程可知,HBase只是增加資料,沒有更新和刪除操作,使用者的更新和刪除都是邏輯層面的,在物理層面,更新只是追加操作,刪除只是標記操作。

    使用者寫操作只需要進入到記憶體即可立即返回,從而保證I/O高效能。

    2. HDFS和HBase各自使用場景

    首先一點需要明白:Hbase是基於HDFS來儲存的。

    HDFS:

    1. 一次性寫入,多次讀取。

    2. 保證資料的一致性。

    3. 主要是可以部署在許多廉價機器中,透過多副本提高可靠性,提供了容錯和恢復機制。

    HBase:

    1. 瞬間寫入量很大,資料庫不好支撐或需要很高成本支撐的場景。

    2. 資料需要長久儲存,且量會持久增長到比較大的場景。

    3. HBase不適用與有 join,多級索引,表關係複雜的資料模型。

    4. 大資料量(100s TB級資料)且有快速隨機訪問的需求。如:淘寶的交易歷史記錄。資料量巨大無容置疑,面向普通使用者的請求必然要即時響應。

    5. 業務場景簡單,不需要關聯式資料庫中很多特性(例如交叉列、交叉表,事務,連線等等)。

    3. Hbase的儲存結構

    Hbase 中的每張表都透過行鍵(rowkey)按照一定的範圍被分割成多個子表(HRegion),預設一個HRegion 超過256M 就要被分割成兩個,由HRegionServer管理,管理哪些 HRegion 由 Hmaster 分配。HRegion 存取一個子表時,會建立一個 HRegion 物件,然後對錶的每個列族(Column Family)建立一個 store 例項, 每個 store 都會有 0 個或多個 StoreFile 與之對應,每個 StoreFile 都會對應一個HFile,HFile 就是實際的儲存檔案,一個 HRegion 還擁有一個 MemStore例項。

    4. 熱點現象(資料傾斜)怎麼產生的,以及解決方法有哪些

    熱點現象

    某個小的時段內,對HBase的讀寫請求集中到極少數的Region上,導致這些region所在的RegionServer處理請求量驟增,負載量明顯偏大,而其他的RgionServer明顯空閒。

    熱點現象出現的原因

    HBase中的行是按照rowkey的字典順序排序的,這種設計最佳化了scan操作,可以將相關的行以及會被一起讀取的行存取在臨近位置,便於scan。然而糟糕的rowkey設計是熱點的源頭。

    熱點發生在大量的client直接訪問叢集的一個或極少數個節點(訪問可能是讀,寫或者其他操作)。大量訪問會使熱點region所在的單個機器超出自身承受能力,引起效能下降甚至region不可用,這也會影響同一個RegionServer上的其他region,由於主機無法服務其他region的請求。

    熱點現象解決辦法

    為了避免寫熱點,設計rowkey使得不同行在同一個region,但是在更多資料情況下,資料應該被寫入叢集的多個region,而不是一個。常見的方法有以下這些:

    1. 加鹽:在rowkey的前面增加隨機數,使得它和之前的rowkey的開頭不同。分配的字首種類數量應該和你想使用資料分散到不同的region的數量一致。加鹽之後的rowkey就會根據隨機生成的字首分散到各個region上,以避免熱點。

    2. 雜湊:雜湊可以使負載分散到整個叢集,但是讀卻是可以預測的。使用確定的雜湊可以讓客戶端重構完整的rowkey,可以使用get操作準確獲取某一個行資料

    3. 反轉:第三種防止熱點的方法時反轉固定長度或者數字格式的rowkey。這樣可以使得rowkey中經常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機rowkey,但是犧牲了rowkey的有序性。反轉rowkey的例子以手機號為rowkey,可以將手機號反轉後的字串作為rowkey,這樣的就避免了以手機號那樣比較固定開頭導致熱點問題

    4. 時間戳反轉:一個常見的資料處理問題是快速獲取資料的最近版本,使用反轉的時間戳作為rowkey的一部分對這個問題十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以透過scan [key]獲得[key]的第一條記錄,因為HBase中rowkey是有序的,第一條記錄是最後錄入的資料。

    • 比如需要儲存一個使用者的操作記錄,按照操作時間倒序排序,在設計rowkey的時候,可以這樣設計[userId反轉] [Long.Max_Value - timestamp],在查詢使用者的所有操作記錄資料的時候,直接指定反轉後的userId,startRow是[userId反轉][000000000000],stopRow是[userId反轉][Long.Max_Value - timestamp]

    • 如果需要查詢某段時間的操作記錄,startRow是[user反轉][Long.Max_Value - 起始時間],stopRow是[userId反轉][Long.Max_Value - 結束時間]

  • HBase建表預分割槽:建立HBase表時,就預先根據可能的RowKey劃分出多個region而不是預設的一個,從而可以將後續的讀寫操作負載均衡到不同的region上,避免熱點現象。

  • 5. HBase的 rowkey 設計原則

    長度原則:100位元組以內,8的倍數最好,可能的情況下越短越好。因為HFile是按照 keyvalue 儲存的,過長的rowkey會影響儲存效率;其次,過長的rowkey在memstore中較大,影響緩衝效果,降低檢索效率。最後,作業系統大多為64位,8的倍數,充分利用作業系統的最佳效能。

    雜湊原則:高位雜湊,低位時間欄位。避免熱點問題。

    唯一原則:分利用這個排序的特點,將經常讀取的資料儲存到一塊,將最近可能會被訪問 的資料放到一塊。

    6. HBase的列簇設計

    原則:在合理範圍內能儘量少的減少列簇就儘量減少列簇,因為列簇是共享region的,每個列簇資料相差太大導致查詢效率低下。

    最優:將所有相關性很強的 key-value 都放在同一個列簇下,這樣既能做到查詢效率最高,也能保持儘可能少的訪問不同的磁碟檔案。以使用者資訊為例,可以將必須的基本資訊存放在一個列族,而一些附加的額外資訊可以放在另一列族。

    7. HBase 中 compact 用途是什麼,什麼時候觸發,分為哪兩種,有什麼區別

    在 hbase 中每當有 memstore 資料 flush 到磁碟之後,就形成一個 storefile,當 storeFile的數量達到一定程度後,就需要將 storefile 檔案來進行 compaction 操作。

    Compact 的作用:

    1. 合併檔案

    2. 清除過期,多餘版本的資料

    3. 提高讀寫資料的效率 4 HBase 中實現了兩種 compaction 的方式:minor and major. 這兩種 compaction 方式的 區別是:

    4. Minor 操作只用來做部分檔案的合併操作以及包括 minVersion=0 並且設定 ttl 的過 期版本清理,不做任何刪除資料、多版本資料的清理工作。

    5. Major 操作是對 Region 下的 HStore 下的所有 StoreFile 執行合併操作,最終的結果 是整理合併出一個檔案。

    Flink

    1. 簡單介紹一下Flink

    Flink是一個面向 流處理批處理的分散式資料計算引擎,能夠基於同一個Flink執行,可以提供流處理和批處理兩種型別的功能。 在 Flink 的世界觀中,一切都是由流組成的,離線資料是有界的流;實時資料是一個沒有界限的流:這就是所謂的有界流和無界流。

    2. Flink的執行必須依賴Hadoop元件嗎

    Flink可以完全獨立於Hadoop,在不依賴Hadoop元件下執行。但是做為大資料的基礎設施,Hadoop體系是任何大資料框架都繞不過去的。Flink可以整合眾多Hadooop 元件,例如Yarn、Hbase、HDFS等等。例如,Flink可以和Yarn整合做資源排程,也可以讀寫HDFS,或者利用HDFS做檢查點。

    3. Flink叢集執行時角色

    Flink 執行時由兩種型別的程式組成: 一個 JobManager 和一個或者多個 TaskManager

    Client 不是執行時和程式執行的一部分,而是用於準備資料流並將其傳送給 JobManager。之後,客戶端可以斷開連線(分離模式),或保持連線來接收程式報告(附加模式)。客戶端可以作為觸發執行 Java/Scala 程式的一部分執行,也可以在命令列程式  ./bin/flink run ... 中執行。

    可以透過多種方式啟動 JobManager 和 TaskManager:直接在機器上作為 standalone 叢集啟動、在容器中啟動、或者透過YARN等資源框架管理並啟動。TaskManager 連線到 JobManagers,宣佈自己可用,並被分配工作。

    JobManager:

    JobManager 具有許多與協調 Flink 應用程式的分散式執行有關的職責:它決定何時排程下一個 task(或一組 task)、對完成的 task 或執行失敗做出反應、協調 checkpoint、並且協調從失敗中恢復等等。這個程式由三個不同的元件組成:

    • ResourceManager

    ResourceManager 負責 Flink 叢集中的資源提供、回收、分配,管理 task slots。

    • Dispatcher

    Dispatcher 提供了一個 REST 介面,用來提交 Flink 應用程式執行,併為每個提交的作業啟動一個新的 JobMaster。它還執行 Flink WebUI 用來提供作業執行資訊。

    • JobMaster

    JobMaster 負責管理單個JobGraph的執行。Flink 叢集中可以同時執行多個作業,每個作業都有自己的 JobMaster。

    TaskManagers

    TaskManager(也稱為 worker)執行作業流的 task,並且快取和交換資料流。

    必須始終至少有一個 TaskManager。在 TaskManager 中資源排程的最小單位是 task slot。TaskManager 中 task slot 的數量表示併發處理 task 的數量。請注意一個 task slot 中可以執行多個運算元。

    4. Flink相比Spark Streaming有什麼區別

    1. 架構模型

    Spark Streaming 在執行時的主要角色包括:Master、Worker、Driver、Executor,Flink 在執行時主要包含:Jobmanager、Taskmanager 和 Slot。

    2. 任務排程

    Spark Streaming 連續不斷的生成微小的資料批次,構建有向無環圖 DAG,Spark Streaming 會依次建立 DStreamGraph、JobGenerator、JobScheduler。

    Flink 根據使用者提交的程式碼生成 StreamGraph,經過最佳化生成 JobGraph,然後提交給 JobManager 進行處理,JobManager 會根據 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 排程最核心的資料結構,JobManager 根據 ExecutionGraph 對 Job 進行排程。

    3. 時間機制

    Spark Streaming 支援的時間機制有限,只支援處理時間。Flink 支援了流處理程式在時間上的三個定義:處理時間、事件時間、注入時間。同時也支援 watermark 機制來處理滯後資料。

    4. 容錯機制

    對於 Spark Streaming 任務,我們可以設定 checkpoint,然後假如發生故障並重啟,我們可以從上次 checkpoint 之處恢復,但是這個行為只能使得資料不丟失,可能會重複處理,不能做到恰一次處理語義。

    Flink 則使用兩階段提交協議來解決這個問題。

    5. 介紹下Flink的容錯機制(checkpoint)

    Checkpoint機制是Flink可靠性的基石,可以保證Flink叢集在某個運算元因為某些原因(如 異常退出)出現故障時,能夠將整個應用流圖的狀態恢復到故障之前的某一狀態,保證應用流圖狀態的一致性。Flink的Checkpoint機制原理來自“Chandy-Lamport algorithm”演算法。

    每個需要Checkpoint的應用在啟動時,Flink的JobManager為其建立一個 CheckpointCoordinator(檢查點協調器),CheckpointCoordinator全權負責本應用的快照製作。

    CheckpointCoordinator(檢查點協調器),CheckpointCoordinator全權負責本應用的快照製作。

    1. CheckpointCoordinator(檢查點協調器) 週期性的向該流應用的所有source運算元傳送 barrier(屏障)。

    2. 當某個source運算元收到一個barrier時,便暫停資料處理過程,然後將自己的當前狀態製作成快照,並儲存到指定的持久化儲存中,最後向CheckpointCoordinator報告自己快照製作情況,同時向自身所有下游運算元廣播該barrier,恢復資料處理

    3. 下游運算元收到barrier之後,會暫停自己的資料處理過程,然後將自身的相關狀態製作成快照,並儲存到指定的持久化儲存中,最後向CheckpointCoordinator報告自身快照情況,同時向自身所有下游運算元廣播該barrier,恢復資料處理。

    4. 每個運算元按照步驟3不斷製作快照並向下遊廣播,直到最後barrier傳遞到sink運算元,快照製作完成。

    5. 當CheckpointCoordinator收到所有運算元的報告之後,認為該週期的快照製作成功; 否則,如果在規定的時間內沒有收到所有運算元的報告,則認為本週期快照製作失敗。

    文章推薦

    Flink可靠性的基石-checkpoint機制詳細解析

    6. Flink checkpoint與Spark Streaming的有什麼區別或優勢嗎

    spark streaming 的 checkpoint 僅僅是針對 driver 的故障恢復做了資料和後設資料的 checkpoint。而 flink 的 checkpoint 機制 要複雜了很多,它採用的是輕量級的分散式快照,實現了每個運算元的快照,及流動中的資料的快照。

    7. Flink是如何保證Exactly-once語義的

    Flink透過實現 兩階段提交和狀態儲存來實現端到端的一致性語義。分為以下幾個步驟:

    開始事務(beginTransaction)建立一個臨時資料夾,來寫把資料寫入到這個資料夾裡面

    預提交(preCommit)將記憶體中快取的資料寫入檔案並關閉

    正式提交(commit)將之前寫完的臨時檔案放入目標目錄下。這代表著最終的資料會有一些延遲

    丟棄(abort)丟棄臨時檔案

    若失敗發生在預提交成功後,正式提交前。可以根據狀態來提交預提交的資料,也可刪除預提交的資料。

    兩階段提交協議詳解八張圖搞懂Flink的Exactly-once

    8. 如果下級儲存不支援事務,Flink怎麼保證exactly-once

    端到端的exactly-once對sink要求比較高,具體實現主要有冪等寫入和事務性寫入兩種方式。

    冪等寫入的場景依賴於業務邏輯,更常見的是用事務性寫入。而事務性寫入又有預寫日誌(WAL)和兩階段提交(2PC)兩種方式。

    如果外部系統不支援事務,那麼可以用預寫日誌的方式,把結果資料先當成狀態儲存,然後在收到 checkpoint 完成的通知時,一次性寫入 sink 系統。

    9. Flink常用的運算元有哪些

    分兩部分:

    1. 資料讀取,這是Flink流計算應用的起點,常用運算元有:
    • 從記憶體讀:fromElements

    • 從檔案讀:readTextFile

    • Socket 接入 :socketTextStream

    • 自定義讀取:createInput

    1. 處理資料的運算元,常用的運算元包括:Map(單輸入單輸出)、FlatMap(單輸入、多輸出)、Filter(過濾)、KeyBy(分組)、Reduce(聚合)、Window(視窗)、Connect(連線)、Split(分割)等。

    推薦閱讀: 一文學完Flink流計算常用運算元(Flink運算元大全)

    10. Flink任務延時高,如何入手

    在 Flink 的後臺任務管理中,我們可以看到 Flink 的哪個運算元和 task 出現了反壓。最主要的手段是資源調優和運算元調優。資源調優即是對作業中的 Operator 的併發數(parallelism)、CPU(core)、堆記憶體(heap_memory)等引數進行調優。作業引數調優包括:並行度的設定,State 的設定,checkpoint 的設定。

    11. Flink是如何處理反壓的

    Flink 內部是基於 producer-consumer 模型來進行訊息傳遞的,Flink的反壓設計也是基於這個模型。Flink 使用了高效有界的分散式阻塞佇列,就像 Java 通用的阻塞佇列(BlockingQueue)一樣。下游消費者消費變慢,上游就會受到阻塞。

    12. 如何排查生產環境中的反壓問題

    1. 反壓出現的場景

    反壓經常出現在促銷、熱門活動等場景。短時間內流量陡增造成資料的堆積或者消費速度變慢。

    它們有一個共同的特點:資料的消費速度小於資料的生產速度。

    2. 反壓監控方法

    透過Flink Web UI發現反壓問題。

    Flink 的 TaskManager 會每隔 50 ms 觸發一次反壓狀態監測,共監測 100 次,並將計算結果反饋給 JobManager,最後由 JobManager 進行計算反壓的比例,然後進行展示。

    這個比例展示邏輯如下:

    OK: 0 <= Ratio <= 0.10,表示狀態良好正;

    LOW: 0.10 < Ratio <= 0.5,表示有待觀察;

    HIGH: 0.5 < Ratio <= 1,表示要處理了(增加並行度/subTask/檢查是否有資料傾斜/增加記憶體)。

    0.01,代表100次中有一次阻塞在內部呼叫。

    3. flink反壓的實現方式

    Flink任務的組成由基本的“流”和“運算元”構成,“流”中的資料在“運算元”間進行計算和轉換時,會被放入分散式的阻塞佇列中。當消費者的阻塞佇列滿時,則會降低生產者的資料生產速度

    4. 反壓問題定位和處理

    Flink會因為資料堆積和處理速度變慢導致checkpoint超時,而checkpoint是Flink保證資料一致性的關鍵所在,最終會導致資料的不一致發生。

    資料傾斜:可以在 Flink 的後臺管理頁面看到每個 Task 處理資料的大小。當資料傾斜出現時,通常是簡單地使用類似 KeyBy 等分組聚合函式導致的,需要使用者將熱點 Key 進行預處理,降低或者消除熱點 Key 的影。

    GC:不合理的設定 TaskManager 的垃圾回收引數會導致嚴重的 GC 問題,我們可以透過  -XX:+PrintGCDetails 引數檢視 GC 的日誌。

    程式碼本身:開發者錯誤地使用 Flink 運算元,沒有深入瞭解運算元的實現機制導致效能問題。我們可以透過檢視執行機器節點的 CPU 和記憶體情況定位問題。

    13. Flink中的狀態儲存

    Flink在做計算的過程中經常需要儲存中間狀態,來避免資料丟失和狀態恢復。選擇的狀態儲存策略不同,會影響狀態持久化如何和 checkpoint 互動。Flink提供了三種狀態儲存方式: MemoryStateBackend、FsStateBackend、RocksDBStateBackend

    14. Operator Chains(運算元鏈)這個概念你瞭解嗎

    為了更高效地分散式執行,Flink 會盡可能地將 operator 的 subtask 連結(chain)在一起形成 task。每個 task 在一個執行緒中執行。將 operators 連結成 task 是非常有效的最佳化:它能減少執行緒之間的切換,減少訊息的序列化/反序列化,減少資料在緩衝區的交換,減少了延遲的同時提高整體的吞吐量。這就是我們所說的運算元鏈。

    15. Flink的記憶體管理是如何做的

    Flink 並不是將大量物件存在堆上,而是將物件都序列化到一個預分配的記憶體塊上。此外,Flink大量的使用了堆外記憶體。如果需要處理的資料超出了記憶體限制,則會將部分資料儲存到硬碟上。Flink 為了直接操作二進位制資料實現了自己的序列化框架。

    16. 如何處理生產環境中的資料傾斜問題

    1. flink資料傾斜的表現

    任務節點頻繁出現反壓,增加並行度也不能解決問題;

    部分節點出現OOM異常,是因為大量的資料集中在某個節點上,導致該節點記憶體被爆,任務失敗重啟。

    2. 資料傾斜產生的原因

    業務上有嚴重的資料熱點,比如滴滴叫車的訂單資料中北京、上海等幾個城市的訂單量遠遠超過其他地區;

    技術上大量使用了 KeyBy、GroupBy 等操作,錯誤的使用了分組 Key,人為產生資料熱點。

    3. 解決問題的思路

    業務上要儘量避免熱點 key 的設計,例如我們可以把北京、上海等熱點城市分成不同的區域,並進行單獨處理;

    技術上出現熱點時,要調整方案打散原來的 key,避免直接聚合;此外 Flink 還提供了大量的功能可以避免資料傾斜。

    17. Flink中的Time有哪幾種

    Flink中的時間有三種型別,如下圖所示:

    • Event Time:是事件建立的時間。它通常由事件中的時間戳描述,例如採集的日誌資料中,每一條日誌都會記錄自己的生成時間,Flink透過時間戳分配器訪問事件時間戳。

    • Ingestion Time:是資料進入Flink的時間。

    • Processing Time:是每一個執行基於時間操作的運算元的本地系統時間,與機器相關,預設的時間屬性就是Processing Time。

    例如,一條日誌進入Flink的時間為 2021-01-22 10:00:00.123,到達Window的系統時間為 2021-01-22 10:00:01.234,日誌的內容如下:
    2021-01-06 18:37:15.624 INFO Fail over to rm2

    對於業務來說,要統計1min內的故障日誌個數,哪個時間是最有意義的?—— eventTime,因為我們要根據日誌的生成時間進行統計。

    18. Flink對於遲到資料是怎麼處理的

    Flink中 WaterMark 和 Window 機制解決了流式資料的亂序問題,對於因為延遲而順序有誤的資料,可以根據eventTime進行業務處理,對於延遲的資料Flink也有自己的解決辦法,主要的辦法是給定一個允許延遲的時間,在該時間範圍內仍可以接受處理延遲資料

    設定允許延遲的時間是透過allowedLateness(lateness: Time)設定

    儲存延遲資料則是透過sideOutputLateData(outputTag: OutputTag[T])儲存

    獲取延遲資料是透過DataStream.getSideOutput(tag: OutputTag[X])獲取

    文章推薦

    Flink 中極其重要的 Time 與 Window 詳細解析

    19. Flink中window出現資料傾斜怎麼解決

    window 產生資料傾斜指的是資料在不同的視窗內堆積的資料量相差過多。本質上產生這種情況的原因是資料來源頭髮送的資料量速度不同導致的。出現這種情況一般透過兩種方式來解決:

    • 在資料進入視窗前做預聚合

    • 重新設計視窗聚合的 key

    20. Flink CEP程式設計中當狀態沒有到達的時候會將資料儲存在哪裡

    在流式處理中,CEP 當然是要支援 EventTime 的,那麼相對應的也要支援資料的遲到現象,也就是watermark的處理邏輯。CEP對未匹配成功的事件序列的處理,和遲到資料是類似的。在 Flink CEP的處理邏輯中,狀態沒有滿足的和遲到的資料,都會儲存在一個Map資料結構中,也就是說,如果我們限定判斷事件序列的時長為5分鐘,那麼記憶體中就會儲存5分鐘的資料,這在我看來,也是對記憶體的極大損傷之一。

    推薦閱讀: 一文學會Flink CEP

    21. Flink設定並行度的方式

    們在實際生產環境中可以從四個不同層面設定並行度:

    1. 操作運算元層面(Operator Level)
    .map(
    new 
    RollingAdditionMapper()).setParallelism(
    10//將操作運算元設定並行度
    
    1. 執行環境層面(Execution Environment Level)
    $
    FLINK_HOME/bin/flink 的-p引數修改並行度
    
    1. 客戶端層面(Client Level)
    env.setParallelism(
    10)
    
    1. 系統層面(System Level)

    全域性配置在flink-conf.yaml檔案中,parallelism.default,預設是1:可以設定預設值大一點

    需要注意的優先順序: 運算元層面>環境層面>客戶端層面>系統層面

    22. Flink中Task如何做到資料交換

    在一個 Flink Job 中,資料需要在不同的 task 中進行交換,整個資料交換是有 TaskManager 負責的,TaskManager 的網路元件首先從緩衝 buffer 中收集 records,然後再傳送。Records 並不是一個一個被髮送的,是積累一個批次再傳送,batch 技術可以更加高效的利用網路資源。

    23. Flink的記憶體管理是如何做的

    Flink 並不是將大量物件存在堆上,而是將物件都序列化到一個預分配的記憶體塊上。此外,Flink大量的使用了堆外記憶體。如果需要處理的資料超出了記憶體限制,則會將部分資料儲存到硬碟上。Flink 為了直接操作二進位制資料實現了自己的序列化框架。

    24. 介紹下Flink的序列化

    Flink 摒棄了 Java 原生的序列化方法,以獨特的方式處理資料型別和序列化,包含自己的型別描述符,泛型型別提取和型別序列化框架。

    TypeInformation 是所有型別描述符的基類。它揭示了該型別的一些基本屬性,並且可以生成序列化器。

    TypeInformation 支援以下幾種型別:

    • BasicTypeInfo: 任意 Java 基本型別或 String 型別

    • BasicArrayTypeInfo: 任意 Java 基本型別陣列或 String 陣列

    • WritableTypeInfo: 任意 Hadoop Writable 介面的實現類

    • TupleTypeInfo: 任意的 Flink Tuple 型別(支援 Tuple1 to Tuple25)。Flink tuples 是固定長度固定型別的 Java Tuple 實現

    • CaseClassTypeInfo: 任意的 Scala CaseClass(包括 Scala tuples)

    • PojoTypeInfo: 任意的 POJO (Java or Scala),例如,Java 物件的所有成員變數,要麼是 public 修飾符定義,要麼有 getter/setter 方法

    • GenericTypeInfo: 任意無法匹配之前幾種型別的類

    25. Flink海量資料高效去重

    1. 基於狀態後端。

    2. 基於HyperLogLog:不是精準的去重。

    3. 基於布隆過濾器(BloomFilter);快速判斷一個key是否存在於某容器,不存在就直接返回。

    4. 基於BitMap;用一個bit位來標記某個元素對應的Value,而Key即是該元素。由於採用了Bit為單位來儲存資料,因此可以大大節省儲存空間。

    5. 基於外部資料庫;選擇使用Redis或者HBase儲存資料,我們只需要設計好儲存的Key即可,不需要關心Flink任務重啟造成的狀態丟失問題。

    26. Flink SQL的是如何實現的

    構建抽象語法樹的事情交給了 Calcite 去做。SQL query 會經過 Calcite 解析器轉變成 SQL 節點樹,透過驗證後構建成 Calcite 的抽象語法樹(也就是圖中的 Logical Plan)。另一邊,Table API 上的呼叫會構建成 Table API 的抽象語法樹,並透過 Calcite 提供的 RelBuilder 轉變成 Calcite 的抽象語法樹。然後依次被轉換成邏輯執行計劃和物理執行計劃。

    在提交任務後會分發到各個 TaskManager 中執行,在執行時會使用 Janino 編譯器編譯程式碼後執行。

    業務方面

    1. ODS層採用什麼壓縮方式和儲存格式?

    壓縮採用 Snappy,儲存採用 orc,壓縮比是100g資料壓縮完10g左右。

    2. DWD層做了哪些事?

    1. 資料清洗
    • 空值去除
    • 過濾核心欄位無意義的資料,比如訂單表中訂單id為null,支付表中支付id為空
    • 對手機號、身份證號等敏感資料脫敏
    • 對業務資料傳過來的表進行維度退化和降維。
    • 將使用者行為寬表和業務表進行資料一致性處理
    1. 清洗的手段
    • Sql、mr、rdd、kettle、Python(專案中採用sql進行清除)

    3. DWS層做了哪些事?

    1. DWS層有3-5張寬表(處理100-200個指標   70%以上的需求)

    具體寬表名稱:使用者行為寬表,使用者購買商品明細行為寬表,商品寬表,購物車寬表,物流寬表、登入註冊、售後等。

    1. 哪個寬表最寬?大概有多少個欄位?最寬的是使用者行為寬表。大概有60-100個欄位

    1. 在處理大資料過程中,如何保證得到期望值

    1. 保證在資料採集的時候不丟失資料,這個尤為重要,如果在資料採集的時候就已經不準確,後面很難達到期望值

    2. 在資料處理的時候不丟失資料,例如sparkstreaming處理kafka資料的時候,要保證資料不丟失,這個尤為重要

    3. 前兩步中,如果無法保證資料的完整性,那麼就要透過離線計算進行資料的校對,這樣才能保證我們能夠得到期望值

    2. 你感覺數倉建設中最重要的是什麼

    數倉建設中,最重要的是資料準確性,資料的真正價值在於資料驅動決策,透過資料指導運營,在一個不準確的資料驅動下,得到的一定是錯誤的資料分析,影響的是公司的業務發展決策,最終導致公司的策略調控失敗。

    3. 資料倉儲建模怎麼做的

    數倉建設中最常用模型--Kimball維度建模詳解

    4. 資料質量怎麼監控

    單表資料量監控

    一張表的記錄數在一個已知的範圍內,或者上下浮動不會超過某個閾值

    1. SQL結果:var 資料量 = select count(*)from 表 where 時間等過濾條件

    2. 報警觸發條件設定:如果資料量不在[數值下限, 數值上限], 則觸發報警

    3. 同比增加:如果((本週的資料量 -上週的資料量)/上週的資料量*100)不在 [比例下線,比例上限],則觸發報警

    4. 環比增加:如果((今天的資料量 - 昨天的資料量)/昨天的資料量*100)不在 [比例下線,比例上限],則觸發報警

    5. 報警觸發條件設定一定要有。如果沒有配置的閾值,不能做監控 日活、周活、月活、留存(日周月)、轉化率(日、周、月)GMV(日、周、月) 復購率(日周月)

    單表空值檢測

    某個欄位為空的記錄數在一個範圍內,或者佔總量的百分比在某個閾值範圍內

    1. 目標欄位:選擇要監控的欄位,不能選“無”

    2. SQL結果:var 異常資料量 = select count(*) from 表 where 目標欄位 is null

    3. 單次檢測:如果(異常資料量)不在[數值下限, 數值上限],則觸發報警

    單表重複值檢測

    一個或多個欄位是否滿足某些規則

    1. 目標欄位:第一步先正常統計條數;select count(*) form 表;

    2. 第二步,去重統計;select count(*) from 表 group by 某個欄位

    3. 第一步的值和第二步的值做減法,看是否在上下線閥值之內

    4. 單次檢測:如果(異常資料量)不在[數值下限, 數值上限], 則觸發報警

    跨表資料量對比

    主要針對同步流程,監控兩張表的資料量是否一致

    1. SQL結果:count(本表) - count(關聯表)

    2. 閾值配置與“空值檢測”相同

    5. 資料分析方法論瞭解過哪些?

    資料商業分析的目標是利用大資料為所有職場人員做出迅捷,高質,高效的決策提供可規模化的解決方案。商業分析是創造價值的資料科學。

    資料商業分析中會存在很多判斷:

    1. 觀察資料當前發生了什麼?

    比如想知道線上渠道A、B各自帶來了多少流量,新上線的產品有多少使用者喜歡,新註冊流中註冊的人數有多少。這些都需要透過資料來展示結果。

    1. 理解為什麼發生?

    我們需要知道渠道A為什麼比渠道B好,這些是要透過資料去發現的。也許某個關鍵字帶來的流量轉化率比其他都要低,這時可以透過資訊、知識、資料沉澱出發生的原因是什麼。

    1. 預測未來會發生什麼?

    在對渠道A、B有了判斷之後,根據以往的知識預測未來會發生什麼。在投放渠道C、D的時候,猜測渠道C比渠道D好,當上線新的註冊流、新的最佳化,可以知道哪一個節點比較容易出問題,這些都是透過資料進行預測的過程。

    1. 商業決策

    所有工作中最有意義的還是商業決策,透過資料來判斷應該做什麼。這是商業分析最終的目的。

    演算法

    大資料面試中考察的演算法相對容易一些,常考的有排序演算法,查詢演算法,二叉樹等,下面講解一些最容易考的演算法。

    1. 排序演算法

    十種常見排序演算法可以分為兩大類:

    • 比較類排序:透過比較來決定元素間的相對次序,由於其時間複雜度不能突破O(nlogn),因此也稱為非線性時間比較類排序。

    • 非比較類排序:不透過比較來決定元素間的相對次序,它可以突破基於比較排序的時間下界,以線性時間執行,因此也稱為線性時間非比較類排序。

    演算法複雜度

    相關概念

    • 穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面。

    • 不穩定:如果a原本在b的前面,而a=b,排序之後 a 可能會出現在 b 的後面。

    • 時間複雜度:對排序資料的總的操作次數。反映當n變化時,操作次數呈現什麼規律。

    • 空間複雜度:是指演算法在計算機內執行時所需儲存空間的度量,它也是資料規模n的函式。

    下面講解大資料中最常考的兩種: 快排和歸併

    1) 快速排序

    快速排序的基本思想:透過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。

    演算法描述

    快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體演算法描述如下:

    • 從數列中挑出一個元素,稱為 “基準”(pivot);

    • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作;

    • 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。



    來自 “ 五分鐘學大資料 ”, 原文作者:園陌;原文連結:https://mp.weixin.qq.com/s/SjhcJdylGPYnS9fw7MZI5g,如有侵權,請聯絡管理員刪除。

    相關文章