關於hbase的read操作的深入研究 region到storefile過程

轉自:http://www.blogjava.net/hello-yun/archive/2012/07/18/383425.html

這裏面說的read既包括get,也包括scan,實際底層來看這兩個操作也是一樣的。

我們將要討論的是,當我們從一張表讀取數據的時候hbase到底是怎麼處理的。
分二種情況來看,第一種就是表剛創建,所有put的數據還在memstore中,並沒有刷新到hdfs上;第二種情況是,該store已經進行多次的flush操作,產生了多個storefile了。
在具體說明兩種情況前,先考慮下表的region的問題,如果表只有一個region,那麼沒有說的,肯定是要掃描這個唯一的region。假設該表有多個region,此時.META.表就派上用場了,hbase會首先根據你要掃描的數據的rowkey來判斷到底該數據放在哪個region上,該region所在服務器地址,然後把數據讀取的請求發送給該region server。好了,實際對數據訪問的任務都會放在region server上執行,爲了簡單起見,接下來的討論都是在單臺region server上對單個region的操作。
首先來看第一種情況,表剛創建,所有put的數據還在memstore中,並沒有刷新到hdfs上。這個時候數據是在memstore中,並沒有storefile產生,理所當然,hbase要查找memstore來獲得相應的數據。對於memstore或者storefile來說,內存中都有關於rowkey的索引的,所以對於通過rowkey的查詢速度是非常快速的。通過查詢該索引就知道是否存在需要查看的數據,已經該數據在memstore中的位置。通過索引提供的信息就很容易找得到所需要的數據。這種情況很簡單。
在來看第二種情況,該store已經進行多次的flush操作,產生了多個storefile了。那麼數據應該從哪裏查呢?所有的storefile?別忘記還有memstore。此時memstore中可能還會有沒來得及flush的數據呢。如果此時該region還有很多的文件,是不是所有的文件都需要查找呢?hbase在查找先會根據時間戳或者查詢列的信息來進行過濾,過濾掉那些肯定不含有所需數據的storefile或者memstore,儘量把我們的查詢目標範圍縮小。
儘管縮小了,但仍可能會有多個文件需要掃描的。storefile的內部有三維有序的,但是各個storefile之間並不是有序的。比如,storefile1中可能有rowkey爲100到110的記錄,而storefile2可能有rowkey爲105到115的數據,storefile的rowkey的範圍很有可能有交叉。所以查詢數據的過程也不可能是對storefile的順序查找。
hbase會首先查看每個storefile的最小的rowkey,然後按照從小到大的順序進行排序,結果放到一個隊列中,排序的算法就是按照hbase的三維順序,按照rowkey,column,ts進行排序,rowkey和column是升序,而ts是降序。
實際上並不是所有滿足時間戳和列過濾的文件都會加到這個隊列中,hbase會首先對各個storefile中的數據進行探測,只會掃描掃描那些存在比當前查詢的rowkey大的記錄的storefile。舉例來說,我當前要查找的rowkey爲108,storefile1中rowkey範圍爲100~104,storefile2中rowkey的範圍爲105~110,那麼對於storefile1最大的rowkey爲104,小於105,所以不存在比所查rowkey105大的記錄,storefile並不會被加到該隊列中。根據相同的規則,storefile2則會被添加到該隊列中。
隊列有了,下面開始查詢數據,首先通過poll取出隊列的頭storefile,會從storefile讀取一條記錄返回;接下來呢,該storefile的下條記錄並不一定是查詢結果的下一條記錄,因爲隊列的比較順序是比較的每個storefile的第一條符合要求的rowkey。所以,hbase會繼續從隊列中剩下的storefile取第一條記錄,把該記錄與頭storefile的第二條記錄做比較,如果前者大,那麼返回頭storefile的第二條記錄;如果後者大,則會把頭storefile放回隊列重新排序,在重新取隊列的頭storefile。然後重複上面的整個過程。這個過程比較煩,語言描述不清楚,代碼會更加清晰。
這段代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public KeyValue next()  throws IOException {
  if(this.current == null) {
    return null;
  }
  KeyValue kvReturn = this.current.next();
  KeyValue kvNext = this.current.peek();
  if (kvNext == null) {
    this.current.close();
    this.current = this.heap.poll();
  } else {
    KeyValueScanner topScanner = this.heap.peek();
    if (topScanner == null ||
        this.comparator.compare(kvNext, topScanner.peek()) >= 0) {
      this.heap.add(this.current);
      this.current = this.heap.poll();
    }
  }
  return kvReturn;
}


以上的代碼在KeyValueHeap.java類中。
舉個例子來說明:表sunwg01,有兩個storefile,storefile1中包括rowkey100,rowkey110;storefile2中包括rowkey104,rowkey108。我現在執行scan ‘sunwg01′掃描表sunwg01中的所有的記錄。
根據前面提到的排序規則,隊列中會有2個元素,按順序分別爲storefile1,storefile2。
1,取出storefile1中的第一條記錄rowkey100,並返回該結果
2,取出storefile1中的下一條記錄rowkey110,同時取出隊列剩餘storefile的第一條記錄rowkey104,經過比較rowkey110大於rowkey104,則將storefile1放回隊列中
3,因爲隊列是有序的隊列,會重新對storefile進行排序,因爲此時storefile1的最小rowkey爲110,而storefile2的最小rowkey爲104,所以排序的結果爲storefile2,storefile1
4,重複上面的過程,直到查不到記錄爲止。
最後查到的結果爲:rowkey100,rowkey104,rowkey108,rowkey110。
順便說下block cache的事情,當從storefile中讀數據的時候會首先查看block cache中是否有該數據,如果有則直接查block cache,就沒必要查詢hdfs;如果沒有該數據,那麼就只能去查hdfs了。這也是爲了block cache的命中率對性能有很大影響的原因。
上面描述了從hbase中read的基本的過程,還有些細節沒有具體說,但是大概過程應該是都說到了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章