大數據開發工程師面試《一》Shopee蝦皮技術面

1、項目問題

1 作了哪些項目
2 使用什麼技術
3 哪一個是你主導的項目,一共開發多少個接口,項目多長時間,數據庫有多少個表html

2、技術問題

1 用本身擅長的語言實現非遞歸單鏈表反轉 現場手寫
2 Hadoop和spark的主要區別
3 Hadoop中一個大文件進行排序,如何保證總體有序?sort只會保證單個節點的數據有序
4 Hive中有哪些udf
5 Hadoop中文件put get的過程詳細描述
6 Java中有哪些GC算法
7 Java中的弱引用 強引用和軟引用分別在哪些場景中使用java

3、技術問題解析

1 用java實現非遞歸單鏈表反轉node

思路:算法

由於在對鏈表進行反轉的時候,須要更新每個node的「next」值,可是,在更新 next 的值前,咱們須要保存 next 的值,不然咱們沒法繼續。因此,咱們須要兩個指針分別指向前一個節點和後一個節點,每次作完當前節點「next」值更新後,把兩個節點往下移,直到到達最後節點。數據庫

實現代碼以下: 緩存

class Node {
    char value;
    Node next;
}

public Node reverse(Node current) {
    //initialization
    Node previousNode = null;
    Node nextNode = null;
    
    while (current != null) {
        //save the next node
        nextNode = current.next;
        //update the value of "next"
        current.next = previousNode;
        //shift the pointers
        previousNode = current;
        current = nextNode;            
    }
    return previousNode;

}

public class Test{
  public static void main(String[] args) {
    Node head = new Node(0);
    Node node1 = new Node(1);
    Node node2 = new Node(2);
    Node node3 = new Node(3);

    head.setNext(node1);
    node1.setNext(node2);
    node2.setNext(node3);

    // 打印反轉前的鏈表
    Node h = head;
    while (null != h) {
      System.out.print(h.getData() + " ");
      h = h.getNext();
    }
    // 調用反轉方法
    // head = reverse1(head);
    head = reverse(head);

    System.out.println("\n**************************");
    // 打印反轉後的結果
    while (null != head) {
      System.out.print(head.getData() + " ");
      head = head.getNext();
    }
  }
}

 

2 Hadoop和spark的主要區別-這個問題基本都會問到框架

記住3點最重要的不一樣之處:dom

  • spark消除了冗餘的 HDFS 讀寫: Hadoop 每次 shuffle 操做後,必須寫到磁盤,而 Spark 在 shuffle 後不必定落盤,能夠 cache 到內存中,以便迭代時使用。若是操做複雜,不少的 shufle 操做,那麼 Hadoop 的讀寫 IO 時間會大大增長,也是 Hive 更慢的主要緣由了。
  • spark消除了冗餘的 MapReduce 階段: Hadoop 的 shuffle 操做必定連着完整的 MapReduce 操做,冗餘繁瑣。而 Spark 基於 RDD 提供了豐富的算子操做,且 reduce 操做產生 shuffle 數據,能夠緩存在內存中。
  • JVM 的優化: Hadoop 每次 MapReduce 操做,啓動一個 Task 便會啓動一次 JVM,基於進程的操做。而 Spark 每次 MapReduce 操做是基於線程的,只在啓動 Executor 是啓動一次 JVM,內存的 Task 操做是在線程複用的。每次啓動 JVM 的時間可能就須要幾秒甚至十幾秒,那麼當 Task 多了,這個時間 Hadoop 不知道比 Spark 慢了多。

3 Hadoop中一個大文件進行排序,如何保證總體有序函數

 在Spark中使用算子sortByKey()能夠實現按關鍵字排序,那Hadoop中實現全排序呢?oop

咱們知道Mapreduce框架在feed數據給reducer以前會對map output key排序,這種排序機制保證了每個reducer局部有序,hadoop 默認的partitioner是HashPartitioner,它依賴於output key的hashcode,使得相同key會去相同reducer,可是不保證全局有序,若是想要得到全局排序結果(好比獲取top N, bottom N),Hadoop提供TotalOrderPartitioner類用於實現全局排序的功能,而且解決了OOM和數據傾斜的問題。TotalOrderPartitioner 類提供了三個採樣器,分別是:

  • SplitSampler 分片採樣器,從數據分片中採樣數據,該採樣器不適合已經排好序的數據
  • RandomSampler隨機採樣器,按照設置好的採樣率從一個數據集中採樣
  • IntervalSampler間隔採樣機,以固定的間隔從分片中採樣數據,對於已經排好序的數據效果很是好。

具體實現能夠參考https://zhuanlan.zhihu.com/p/43851100

 

4 Hive中有哪些UDF

Hive中有3種UDF:

  • UDF(User-Defined-Function)用戶自定義函數,輸入一個數據而後產生一個數據; 
  • UDAF(User-Defined Aggregation Function)用戶自定義聚合函數,多個輸入數據而後產生一個輸出參數; 
  • UDTF(User-Defined Table-generating Function)用戶自定義表生成函數,輸入一行數據生成N行數據。

你寫過哪些UDF?在哪一種狀況下會使用該UDF?--本身能夠擴展這個問題


5 Hadoop中數據讀寫流程分析,即文件在put get過程當中具體發生了什麼

 i) hadoop fs -put 操做爲例:

  • 當接收到 PUT 請求時,嘗試在 NameNode 中 create 一個新的 INode 節點,這個節點是根據 create 中發送過去的 src 路徑構建出的目標節點,若是發現節點已存在或是節點的 parent 存在且不爲 INodeDirectory 則異常中斷,不然則返回包含 INode 信息的 HdfsFileStatus 對象。

  • 使用 HdfsFileStatus 構造一個實現了 OutputStream 接口的 DFSOutputStream 類,經過 nio 接口將須要傳輸的數據寫入 DFSOutputStream。

  • 在 DFSOutputStream 中寫入的數據被以必定的 size(通常是 64 k)封裝成一個 DFSPacket,壓入 DataStreamer 的傳輸隊列中。

  • DataStreamer 是 Client 中負責數據傳輸的獨立線程,當發現隊列中有 DFSPacket 時,先經過 namenode.addBlock 從 NameNode 中獲取可供傳輸的 DataNode 信息,而後同指定的 DataNode 進行數據傳輸。

  • DataNode 中有一個專門的 DataXceiverServer 負責接收數據,當有數據到來時,就進行對應的 writeBlock 寫入操做,同時若是發現還有下游的 DataNode 一樣須要接收數據,就經過管道再次將發來的數據轉發給下游 DataNode,實現數據的備份,避免經過 Client 一次進行數據發送。

整個操做步驟中的關鍵步驟有 NameNode::addBlock 以及 DataNode::writeBlock, 接下來會對這兩步進行詳細分析。

 ii) hadoop fs -get操做:

GET 操做的流程,相對於 PUT 會比較簡單,先經過參數中的來源路徑從 NameNode 對應 INode 中獲取對應的 Block 位置,而後基於返回的 LocatedBlocks 構造出一個 DFSInputStream 對象。在 DFSInputStream 的 read 方法中,根據 LocatedBlocks 找到擁有 Block 的 DataNode 地址,經過 readBlock 從 DataNode 獲取字節流。


6 Java中有哪些GC算法

現代虛擬機中的垃圾蒐集算法:

  • 標記-清除
  • 複製算法(適合新生代)
  • 標記-壓縮(適合老年代)
  • 分代收集(新生代採用複製算法,老年代採用標記-壓縮算法)

標記 -清除算法

「標記-清除」(Mark-Sweep)算法,如它的名字同樣,算法分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收掉全部被標記的對象。之因此說它是最基礎的收集算法,是由於後續的收集算法都是基於這種思路並對其缺點進行改進而獲得的。

它的主要缺點有兩個:一個是效率問題,標記和清除過程的效率都不高;另一個是空間問題,標記清除以後會產生大量不連續的內存碎片,空間碎片太多可能會致使,當程序在之後的運行過程當中須要分配較大對象時沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。

複製算法

「複製」(Copying)的收集算法,它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。

這樣使得每次都是對其中的一塊進行內存回收,內存分配時也就不用考慮內存碎片等複雜狀況,只要移動堆頂指針,按順序分配內存便可,實現簡單,運行高效。只是這種算法的代價是將內存縮小爲原來的一半,持續複製長生存期的對象則致使效率下降。

標記-壓縮算法

複製收集算法在對象存活率較高時就要執行較多的複製操做,效率將會變低。更關鍵的是,若是不想浪費50%的空間,就須要有額外的空間進行分配擔保,以應對被使用的內存中全部對象都100%存活的極端狀況,因此在老年代通常不能直接選用這種算法。

根據老年代的特色,有人提出了另一種「標記-整理」(Mark-Compact)算法,標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存

分代收集算法

GC分代的基本假設:絕大部分對象的生命週期都很是短暫,存活時間短。

「分代收集」(Generational Collection)算法,把Java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少許存活,那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。而老年代中由於對象存活率高、沒有額外空間對它進行分配擔保,就必須使用「標記-清理」或「標記-整理」算法來進行回收。


7 Java中的弱引用、強引用、軟引用和虛引用是什麼,他們分別在哪些場景中使用

  • 強引用(」Strong」Reference),咱們日常典型編碼 Object obj=newObject() 中的obj就是強引用。經過關鍵字new建立的對象所關聯的引用就是強引用強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。當JVM 內存空間不足,JVM 寧願拋出OutOfMemoryError運行時錯誤(OOM),使程序異常終止,也不會靠隨意回收具備強引用的「存活」對象來解決內存不足的問題。只要還有強引用指向一個對象,就能代表對象還「活着」,垃圾收集器不會碰這種對象。對於一個普通的對象,若是沒有其餘的引用關係,只要超出對象的生命週期範圍或者顯式地將相應(強)引用賦值爲null,就是能夠被垃圾收集的了,固然具體回收時機仍是要看垃圾收集策略。
  • 軟引用(SoftReference),是一種相對強引用弱化一些的引用,可讓對象豁免一些垃圾收集,只有當JVM 認爲內存不足時,纔會去試圖回收軟引用指向的對象。JVM 會確保在拋出OutOfMemoryError以前,清理軟引用指向的對象。軟引用一般用來實現內存敏感的緩存,若是還有空閒內存,就能夠暫時保留緩存,當內存不足時清理掉,這樣就保證了使用緩存的同時,不會耗盡內存。軟引用能夠和一個引用隊(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。後續,咱們能夠調用ReferenceQueue的poll()方法來檢查是否有它所關心的對象被回收。若是隊列爲空,將返回一個null,不然該方法返回隊列中前面的一個Reference對象。【應用場景】:軟引用一般用來實現內存敏感的緩存。若是還有空閒內存,就能夠暫時保留緩存,當內存不足時清理掉,這樣就保證了使用緩存的同時,不會耗盡內存。
  • 弱引用經過WeakReference類實現。弱引用的生命週期比軟引用短。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快回收弱引用的對象。弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。【應用場景】:弱應用一樣可用於內存敏感的緩存。
  • 虛引用,你不能經過它訪問對象。幻象引用僅僅是提供了一種確保對象被finalize之後,作某些事情的機制。虛引用只是用來得知對象是否被GC。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。【應用場景】:可用來跟蹤對象被垃圾回收器回收的活動,當一個虛引用關聯的對象被垃圾收集器回收以前會收到一條系統通知。

經過表格來講明一下,以下:

 

引用類型

被垃圾回收時間

   用途

   生存時間

強引用

歷來不會

對象的通常狀態

JVM中止運行時終止

軟引用

在內存不足時

對象緩存

內存不足時終止

弱引用

在垃圾回收時

對象緩存

gc運行後終止

虛引用

任什麼時候候

跟蹤對象被垃圾回收器回收的活動

Unknown

 

 

=================================================================================

原創文章,轉載請務必將下面這段話置於文章開頭處(保留超連接)。
本文轉發自程序媛說事兒,原文連接http://www.javashuo.com/article/p-xkazofhw-ho.html=================================================================================

相關文章
相關標籤/搜索