HBase中MVCC的實現機制及應用情況

來源:互聯網
上載者:User

標籤:style   blog   http   color   java   使用   os   io   

MVCC(Multi-Version Concurrent Control),即多版本並發控制協議,廣泛使用於資料庫系統。本文將介紹HBase中對於MVCC的實現及應用情況。

MVCC基本原理

在介紹MVCC概念之前,我們先來想一下資料庫系統裡的一個問題:假設有多個使用者同時讀寫資料庫裡的一行記錄,那麼怎麼保證資料的一致性呢?一個基本的解決方案是對這一行記錄加上一把鎖,將不同使用者對同一行記錄的讀寫操作完全序列化執行,由於同一時刻只有一個使用者在操作,因此一致性不存在問題。但是,它存在明顯的效能問題:讀會阻塞寫,寫也會阻塞讀,整個資料庫系統的並發效能將大打折扣。

MVCC(Multi-Version Concurrent Control),即多版本並發控制協議,它的目標是在保證資料一致性的前提下,提供一種高並發的訪問效能。在MVCC協議中,每個使用者在串連資料庫時看到的是一個具有一致性狀態的鏡像,每個事務在提交到資料庫之前對其他使用者均是不可見的。當事務需要更新資料時,不會直接覆蓋以前的資料,而是產生一個新的版本的資料,因此一條資料會有多個版本儲存,但是同一時刻只有最新的版本號碼是有效。因此,讀的時候就可以保證總是以當前時刻的版本的資料可以被讀到,不論這條資料後來是否被修改或刪除。

更多關於MVCC基本思想的介紹,參考Wikipedia。

一個MVCC實作類別

見org.apache.hadoop.hbase.regionserver.MultiVersionConsistencyControl,用於控制Memstore中讀寫的一致性,其中維護兩個long型的變數:

1)memstoreRead:用於記錄當前全域可讀的readPoint,同時為了每個用戶端讀請求能夠記錄自己發起請求時刻的readPoint,還有一個ThreadLocal的perThreadReadPoint變數,以及相關的set和get方法;

2)memstoreWrite:用於記錄當前全域最大的writePoint,根據它為下個事務產生新的writePoint。

MultiVersionConsistencyControl中關鍵的實現方法如下:

1)WriteEntry beginMemstoreInsert():開始一個更新操作,將memstoreWrite加1,建立writeQueue並插入到writeQueue,並返回WriteEntry對象;

2)void completeMemstoreInsert(WriteEntry e):完成當前更新操作,將WriteEntry對象標記為可讀,具體分兩步:

  • boolean advanceMemstore(WriteEntry e):從頭開始遍曆writeQueue,移除所有已完成的WriteEntry對象,最後將memstoreRead更新為最新已完成的memstoreWrite;
  • void waitForRead(WriteEntry e):阻塞當前線程,直到memstoreRead等於當前WriteEntry的memstoreWrite,至此表明當前WriteEntry之前的所有更新事務都已經完成。
MVCC使用情境

見org.apache.hadoop.hbase.regionserver.HRegion.java,每個Region包含一個Memstore,維護一個MultiVersionConsistencyControl對象。

寫操作

見HRegion.java中的以下寫操作的方法:

1)put

2)checkAndPut

3)delete

4)checkAndDelete

5)internalFlushcache

6)mutateRow

7)mutateRowsWithLocks

8)batchMutate

最終會調用到applyFamilyMapToMemstore方法使用MVCC進行寫操作:

  /**   * Atomically apply the given map of family->edits to the memstore.   * This handles the consistency control on its own, but the caller   * should already have locked updatesLock.readLock(). This also does   * <b>not</b> check the families for validity.   *   * @param familyMap Map of kvs per family   * @param localizedWriteEntry The WriteEntry of the MVCC for this transaction.   *        If null, then this method internally creates a mvcc transaction.   * @return the additional memory usage of the memstore caused by the   * new entries.   */  private long applyFamilyMapToMemstore(Map<byte[], List<KeyValue>> familyMap,    MultiVersionConsistencyControl.WriteEntry localizedWriteEntry) {    long size = 0;    boolean freemvcc = false;    try {      if (localizedWriteEntry == null) {        localizedWriteEntry = mvcc.beginMemstoreInsert();        freemvcc = true;      }      for (Map.Entry<byte[], List<KeyValue>> e : familyMap.entrySet()) {        byte[] family = e.getKey();        List<KeyValue> edits = e.getValue();        Store store = getStore(family);        for (KeyValue kv: edits) {          kv.setMemstoreTS(localizedWriteEntry.getWriteNumber());          size += store.add(kv);        }      }    } finally {      if (freemvcc) {        mvcc.completeMemstoreInsert(localizedWriteEntry);      }    }     return size;   }
View Code讀操作

HRegion.java中通過private ConcurrentHashMap<RegionScanner, Long> scannerReadPoints;維護各個查詢請求的readPoint。

以get或scan請求為例,最終會通過getScanner方法需要構造RegionScannerImpl對象:

org.apache.hadoop.hbase.regionserver.HRegion.RegionScannerImpl:

1)根據Scan物件建構時設定好readPoint,scan.getIsolationLevel()分為READ_UNCOMMITTED和READ_COMMITTED,只有當READ_COMMITTED時根據MultiVersionConsistencyControl.resetThreadReadPoint(mvcc);設定當前scanner線程的readPoint,並插入到scannerReadPoints維護起來。

2)根據scan需要讀取的column family,建立StoreScanner(根據bloom filter、time range、ttl篩選需要的MemStoreScanner和StoreFileScanner),添加到scanners中,並最終根據scanners構造出一個KeyValueHeap。

下面看下RegionScannerImpl中的next方法是每次查詢時需要調用的函數:

boolean org.apache.hadoop.hbase.regionserver.HRegion.RegionScannerImpl.next(List<KeyValue> outResults, int limit) throws IOException

而上述方法會通過KeyValueHeap的next方法讀取下一條資料:先定位到當前KeyValueScanner(即之前構造KeyValueHeap時傳入的MemStoreScanner或StoreScanner),然後調用next方法。

StoreFileScanner和MemStoreScanner均為KeyValueScanner,通過其中的next()介面方法,分別調用到StoreFileScanner.java的skipKVsNewerThanReadpoint方法、Memstore.java中MemStoreScanner對象的getNext方法。

1)StoreFileScanner.java的skipKVsNewerThanReadpoint方法:

  protected boolean skipKVsNewerThanReadpoint() throws IOException {    long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();    // We want to ignore all key-values that are newer than our current    // readPoint    while(enforceMVCC        && cur != null        && (cur.getMemstoreTS() > readPoint)) {      hfs.next();      cur = hfs.getKeyValue();    }    if (cur == null) {      close();      return false;    }    // For the optimisation in HBASE-4346, we set the KV‘s memstoreTS to    // 0, if it is older than all the scanners‘ read points. It is possible    // that a newer KV‘s memstoreTS was reset to 0. But, there is an    // older KV which was not reset to 0 (because it was    // not old enough during flush). Make sure that we set it correctly now,    // so that the comparision order does not change.    if (cur.getMemstoreTS() <= readPoint) {      cur.setMemstoreTS(0);    }    return true;  }
View Code

2)  Memstore.java中MemStoreScanner對象的getNext方法:

    protected KeyValue getNext(Iterator<KeyValue> it) {      long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();          while (it.hasNext()) {        KeyValue v = it.next();        if (v.getMemstoreTS() <= readPoint) {          return v;        }      }      return null;    }
View Code

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.