全部產品
Search
文件中心

ApsaraDB for HBase:冷熱分離

更新時間:Jul 06, 2024

ApsaraDB for HBase增強版支援冷熱分離功能,可以將冷熱資料存放區在不同的介質中,有效提升熱資料的查詢效率,同時降低資料存放區成本。

背景資訊

在海量巨量資料情境下,一張表中的部分業務資料隨著時間的推移僅作為歸檔資料或者訪問頻率很低,同時這部分歷史資料體量非常大,比如訂單資料或者監控資料,降低這部分資料的儲存成本將會極大的節省企業的成本。因此,如何以極簡的營運配置最大程度地降低儲存成本,成為了資料庫產品新的課題。為實現這一目標,阿里雲HBase增強版冷熱分離功能應運而生。阿里雲HBase增強版為冷資料提供新的儲存介質,新的儲存介質儲存成本僅為高效雲端硬碟的1/3。

HBase增強版在同一張表裡實現了資料的冷熱分離,系統會自動根據使用者佈建的冷熱分界線自動將表中的冷資料歸檔到冷儲存中。在使用者的訪問方式上和訪問普通表沒有任何差異,在查詢的過程中,使用者只需配置查詢的Hint或者TimeRange,系統會根據條件自動地判斷資料應該在熱資料區還是冷資料區。

原理介紹

使用者在表上配置資料冷熱時間分界點後,HBase增強版會依賴使用者寫入資料的時間戳記(毫秒)和時間分界點來判斷資料的冷熱。資料開始儲存在熱儲存上,隨意時間的推移慢慢往冷儲存上遷移。同時使用者可以任意變更資料的冷熱分界點,資料可以從熱儲存到冷儲存,也可以從冷儲存到熱儲存。原理介紹

注意事項

參見使用冷儲存中的注意事項。

使用方法

冷儲存功能需要HBase增強版服務端升級到2.1.8及以上版本,但無需修改資料讀寫鏈路的用戶端依賴,只需要選擇以下一種方式修改表結構即可:

開通冷儲存功能

請參照使用冷儲存開通叢集的冷儲存功能。

為表設定冷熱分界線

使用者在使用過程中可以隨時調整COLD_BOUNDARY來劃分冷熱的邊界。COLD_BOUNDARY的單位為秒,如COLD_BOUNDARY => 86400 代表86400秒(一天)前寫入的資料會被自動歸檔到冷儲存介質上。

在冷熱分離使用過程中,無需把列簇的屬性設定為COLD,如果已經把列簇的屬性設定為了COLD,請參見使用冷儲存將冷儲存的屬性去除。

Shell

// 建立冷熱分離表
hbase(main):002:0> create 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'86400'}
// 取消冷熱分離
hbase(main):004:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>""}
// 為已經存在的表設定冷熱分離,或者修改冷熱分離分界線,單位為秒
hbase(main):005:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'86400'}

Java API方式

// 建立冷熱分離表
Admin admin = connection.getAdmin();
TableName tableName = TableName.valueOf("chsTable");
HTableDescriptor descriptor = new HTableDescriptor(tableName);
HColumnDescriptor cf = new HColumnDescriptor("f");
// COLD_BOUNDARY 設定冷熱分離時間分界點,單位為秒, 樣本表示1天之前的資料歸檔為冷資料
cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "86400");
descriptor.addFamily(cf);
admin.createTable(descriptor);

// 取消冷熱分離
// 注意:需要做major compaction,資料才能從冷儲存上回到熱儲存上
HTableDescriptor descriptor = admin
    .getTableDescriptor(tableName);
HColumnDescriptor cf = descriptor.getFamily("f".getBytes());
// 取消冷熱分離
cf.setValue(AliHBaseConstants.COLD_BOUNDARY, null);
admin.modifyTable(tableName, descriptor);

// 為已經存在的表設定冷熱分離功能,或者修改冷熱分離分界線
HTableDescriptor descriptor = admin
    .getTableDescriptor(tableName);
HColumnDescriptor cf = descriptor.getFamily("f".getBytes());
// COLD_BOUNDARY 設定冷熱分離時間分界點,單位為秒, 樣本表示1天之前的資料歸檔為冷資料
cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "86400");
admin.modifyTable(tableName, descriptor);

資料寫入

冷熱分離的表與普通表的資料寫入方式完全一致,使用者可以參照使用Java API訪問增強版叢集文檔中的方式或者使用多語言API訪問對錶進行資料寫入。資料的寫入的時間戳記使用的是目前時間。資料先會儲存在熱儲存(雲端硬碟)中。隨著時間的推移,如果這行資料的寫入時間超過COLD_BOUNDARY設定的值,就會在major_compact時歸檔到冷資料,此過程完全對使用者透明。

資料查詢

由於冷熱資料都在同一張表中,使用者全程只需要和一張表互動。在查詢過程中,如果使用者明確知道需要查詢的資料在熱資料裡(寫入時間少於COLD_BOUNDARY設定的值),可以在Get或者Scan上設定HOT_ONLY的Hint來告訴伺服器只查詢熱區資料。或者在Get/Scan上設定TimeRange來限定查詢資料的時間,系統會根據設定TimeRange決定是查詢熱區,冷區還是冷熱都查。查詢冷區資料延遲要比熱區資料延遲高的多,並且查詢吞吐受到冷儲存限制

查詢樣本

Get

  • Shell

    // 不帶HotOnly Hint的查詢,可能會查詢到冷資料
    hbase(main):013:0> get 'chsTable', 'row1'
    // 帶HotOnly Hint的查詢,只會查熱資料部分,如row1是在冷儲存中,該查詢會沒有結果
    hbase(main):015:0> get 'chsTable', 'row1', {HOT_ONLY=>true}
    // 帶TimeRange的查詢,系統會根據設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料(注意TimeRange的單位為毫秒時間戳記)
    hbase(main):016:0> get 'chsTable', 'row1', {TIMERANGE => [0, 1568203111265]}
  • Java

    Table table = connection.getTable("chsTable");
    // 不帶HotOnly Hint的查詢,可能會查詢到冷資料
    Get get = new Get("row1".getBytes());
    System.out.println("result: " + table.get(get));
    // 帶HotOnly Hint的查詢,只會查熱資料部分,如row1是在冷儲存中,該查詢會沒有結果
    get = new Get("row1".getBytes());
    get.setAttribute(AliHBaseConstants.HOT_ONLY, Bytes.toBytes(true));
    // 帶TimeRange的查詢,系統會根據設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料(注意TimeRange的單位為毫秒時間戳記)
    get = new Get("row1".getBytes());
    get.setTimeRange(0, 1568203111265)

Scan

如果scan不設定Hot Only,或者TimeRange包含冷區時間,則會並行訪問冷資料和熱資料來合并結果,這是由於HBase的Scan原理決定的。

  • Shell

    // 不帶HotOnly Hint的查詢,一定會查詢到冷資料
    hbase(main):017:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9'}
    // 帶HotOnly Hint的查詢,只會查詢熱資料部分
    hbase(main):018:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', HOT_ONLY=>true}
    // 帶TimeRange的查詢,系統會根據設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料(注意TimeRange的單位為毫秒時間戳記)
    hbase(main):019:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', TIMERANGE => [0, 1568203111265]}
  • Java

    TableName tableName = TableName.valueOf("chsTable");
    Table table = connection.getTable(tableName);
    // 不帶HotOnly Hint的查詢,一定會查詢到冷資料
    Scan scan = new Scan();
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        System.out.println("scan result:" + result);
    }
    // 帶HotOnly Hint的查詢,只會查詢熱資料部分
    scan = new Scan();
    scan.setAttribute(AliHBaseConstants.HOT_ONLY, Bytes.toBytes(true));
    // 帶TimeRange的查詢,系統會根據設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料(注意TimeRange的單位為毫秒時間戳記)
    scan = new Scan();
    scan.setTimeRange(0, 1568203111265);
說明
  1. 冷熱分離表中的冷區只是用來歸檔資料,查詢請求應該非常的少,使用者查詢冷熱分離表的絕大部分請求應該帶上HOT_ONLY的標記(或者設定的TimeRange只在熱區)。如果使用者有大量請求需要去查冷區資料,則可能得考慮COLD_BOUNDARY冷熱分界線的設定是否合理。

  2. 如果一行資料已經在冷資料區域,但這一行後續有更新,更新的欄位先會在熱區,如果設定HOT_ONLY去查詢這一行(或者設定的TimeRange只在熱區),則只會返回這一行更新的欄位(在熱區)。只有在查詢時去掉HOT_ONLY Hint,去掉TimeRange,或保證TimeRange覆蓋了該行資料插入和更新時間,才能完整返回這一行。因此不建議對已經進入冷區的資料進行更新,如果有頻繁更新冷資料的需求,則可能得考慮COLD_BOUNDARY冷熱分界線的設定是否合理。

查看錶中冷資料和熱資料的大小

叢集管理系統的表Tab中,可以顯示某一張表的冷儲存使用大小和熱儲存使用大小。

說明

如果資料還沒有進入冷儲存,有可能資料還在記憶體中,請執行flush,將資料刷寫到盤上,再請執行major_compact完成後再查看。

User Table

優先查詢熱資料

在範圍查詢(Scan)情境下,查詢的資料可能橫跨冷熱區,比如查詢一個使用者的所有訂單、聊天記錄等。但查詢的結果往往是從新到舊的分頁展示,最先展示的是最近的熱資料。在這個情境下,普通的Scan(不帶Hot_Only)會並行地掃描冷熱資料,導致請求效能下降。而在開啟了優先查詢熱資料後,會優先只查熱資料,只有熱資料的條數不夠顯示(如使用者點了下一頁查看),才會去查詢冷資料,減少冷儲存的訪問,提升請求響應。

開啟熱資料優先查詢,只需在Scan上設定COLD_HOT_MERGE屬性即可。該屬性的含義是優先查詢熱儲存中的資料, 若熱儲存中的資料查完了,使用者仍然在調用next擷取下一條資料,則會開始查詢冷資料。

Shell

hbase(main):002:0> scan 'chsTable', {COLD_HOT_MERGE=>true}

Java

scan = new Scan();
scan.setAttribute(AliHBaseConstants.COLD_HOT_MERGE, Bytes.toBytes(true));
scanner = table.getScanner(scan);
說明
  • 若某一行同時包含熱資料和冷資料(部分列屬於熱資料,部分列屬於冷資料,比如部分列更新情境),開啟熱資料優先功能,會使得該行的查詢結果會分兩次返回,即scanner返回的Result集合中,對於同一個Rowkey會有兩個對應的Result。

  • 由於是先返回熱資料,再返回冷資料,開啟熱資料優先功能後,無法保證後返回的冷資料結果的Rowkey一定大於先返回的熱資料結果的Rowkey,即Scan得到的Result集不保序, 但熱資料和冷資料的各自返回集仍保證按Rowkey有序排列(參見下面的demo)。在部分實際情境中, 使用者可以通過Rowkey設計,保障scan的結果仍然保序,比如訂單記錄表,Rowkey=使用者ID+訂單建立時間,掃描某個使用者的訂單資料是有序的。

//假設rowkey為"coldRow"的這一行是冷資料,rowkey為"hotRow"的這一行為熱資料
//正常情況下,由於hbase的row是字典序排列,rowkey為"coldRow"的這一行會比"hotRow"這一行先返回。
hbase(main):001:0> scan 'chsTable'
ROW                                                                COLUMN+CELL
 coldRow                                                              column=f:value, timestamp=1560578400000, value=cold_value
 hotRow                                                               column=f:value, timestamp=1565848800000, value=hot_value
2 row(s)

// 設定COLD_HOT_MERGE時, scan的rowkey順序被破壞,熱資料比冷資料先返回,因此返回的結果中,"hot"排在了"cold"的前面
hbase(main):002:0> scan 'chsTable', {COLD_HOT_MERGE=>true}
ROW                                                                COLUMN+CELL
 hotRow                                                               column=f:value, timestamp=1565848800000, value=hot_value
 coldRow                                                              column=f:value, timestamp=1560578400000, value=cold_value
2 row(s)