Hadoop3.2.1 【 HDFS 】源碼分析 : BlocksMap解析

一.概述

BlocksMap是Namenode上與數據塊相關的最重要的類, 它管理着Namenode上數據塊的元數據, 包括當前數據塊屬於哪個HDFS文件, 以及當前數據塊保存在哪些Datanode上。 當Datanode啓動時, 會對Datanode的本地磁盤進行掃描, 並將當前Datanode上保存的數據塊信息彙報到Namenode。 Namenode收到Datanode的彙報信息後, 會建立數據塊與保存這個數據塊的數據節點的對應關係, 並將這個信息保存在BlocksMap中。 所以無論是獲取某個數據塊對應的HDFS文件, 還是獲取數據塊保存在哪些數據節點上, 都需要通過BlocksMap對象.

 

 

二. 靜態變量

 

  /** Constant {@link LightWeightGSet} capacity. */
  private final int capacity;
  
  private GSet<Block, BlockInfo> blocks;

  private final LongAdder totalReplicatedBlocks = new LongAdder();
  private final LongAdder totalECBlockGroups = new LongAdder();

這裏面一共有兩個靜態變量比較重要, 一個是 capacity[容量], 另外一個是blocks . 

blocks是存放數據的具體的實現類.但是,我們所看的GSet 只是一個接口了.

三. GSet&LightWeightGSet概述

在上面blocks 字段對應的是GSet<Block, BlockInfo> 類型的數據. 用於存放真實的數據.

在這裏,我們看一下,這個類具體是怎麼是實現的, 或者說 數據是怎麼保存的, 與我們常用的數據結構有什麼不同.

首先說一下GSet和LightWeightGSet的關係. 

GSet是接口. LightWeightGSet是GSet的實現類.

 

 

我們看一下注釋, 這個是描述.

 A low memory footprint {@link GSet} implementation,
 which uses an array for storing the elements and linked lists for collision resolution.

No rehash will be performed.
Therefore, the internal array will never be resized.

This class does not support null element.
This class is not thread safe.

大概的意思就是說.

是一個低內存的佔用的GSet 實現類. 他使用數組來存儲元素, 使用 鏈表 解決碰撞.

他永遠不會調整內部長度的大小, 所以他的大小,在創建的時候進行指定.

因爲不會擴容所以也不會存在擴容引起的問題. 比如重新進行hash值.

這個類不只是爲null的元素, 所以這個類不是線程安全的.

 

 

四. LightWeightGSet實現

LightWeightGSet類實現於GSet接口.採用數據+鏈表的方式進行存儲.

  /**
   * An internal array of entries, which are the rows of the hash table.
   * The size must be a power of two.
   */
  protected LinkedElement[] entries;

核心就是LinkedElement[] entries 數組.   

然後具體是怎麼實現的呢? 我們先看一下構造方法.

  /**
   * @param recommended_length Recommended size of the internal array.
   */
  public LightWeightGSet(final int recommended_length) {
    final int actual = actualArrayLength(recommended_length);
    if (LOG.isDebugEnabled()) {
      LOG.debug("recommended=" + recommended_length + ", actual=" + actual);
    }
    entries = new LinkedElement[actual];
    hash_mask = entries.length - 1;
  }

這裏其實就是對LightWeightGSet進行初始化操作, 首先確定數組的大小,  數據的大小根據入參recommended_length 進行確定.

然後會用actualArrayLength進行處理, 驗證是否在 1 到 2^30 之間.

並處理成一個大於等於且是2的倍數的actual值返回.

然後根據actual值創建一個數組就行了.

 

  @Override
  public E put(final E element) {
    // validate element
    if (element == null) {
      throw new NullPointerException("Null element is not supported.");
    }
    LinkedElement e = null;
    try {
      e = (LinkedElement)element;
    } catch (ClassCastException ex) {
      throw new HadoopIllegalArgumentException(
          "!(element instanceof LinkedElement), element.getClass()="
          + element.getClass());
    }

    // find index
    // 很簡單,就是通過hash值與數組長度取餘,計算出下標就行了.
    final int index = getIndex(element);

    // remove if it already exists
    // 移除已經存在的key
    final E existing = remove(index, element);

    // insert the element to the head of the linked list
    // 將element加入到鏈表的頭裏面, 並將數組的槽位指定到數組中.
    modification++;
    size++;
    e.setNext(entries[index]);
    entries[index] = e;

    return existing;
  }

插入很簡單,就是分以下步驟.

1. 根據key的hash值與數據的長度 通過  [ & ] 計算出對應的數組下標.

2.根據下標尋找數組中是否已經存在相同key的元素, 如果存在則刪除.

3.將元素加到鏈表的頭裏面,並且將數組的槽位指定到數據組中. 跟JDK1.7版本的HashMap實現差不多. 都是數組+鏈表存儲.

 

獲取元素的話,我直接貼代碼了, 就是通過hash值獲取到數組元素的下標, 然後遍歷下標上對應的鏈表.

@Override
  public E get(final K key) {
    //validate key
    if (key == null) {
      throw new NullPointerException("key == null");
    }

    //find element
    final int index = getIndex(key);
    for(LinkedElement e = entries[index]; e != null; e = e.getNext()) {
      if (e.equals(key)) {
        return convert(e);
      }
    }
    //element not found
    return null;
  }

 

 

五.使用限制

 

1.容量在使用的時候必須制定大小.

2. value 必須是key的子類,並且要實現LinkedElement接口.

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章