本文介紹如何按時間戳記劃分冷熱資料。
背景資訊
按時間戳記冷熱分離是以時間戳記為條件,冷熱分離時間點為依據,對資料進行歸檔。未設定自訂時間戳記時,預設時間戳記的值為資料寫入時間。
前提條件
已開通冷儲存功能。如何開通,請參見冷儲存介紹。
已串連寬表引擎。
如何通過HBase Shell串連寬表引擎,請參見通過Lindorm Shell訪問寬表引擎。
如何通過HBase Java API串連寬表引擎,請參見基於HBase Java API的應用開發。
如何通過Lindorm-cli串連寬表引擎,請參見通過Lindorm-cli串連並使用寬表引擎。
表或列簇的儲存屬性STORAGE_POLICY不能為
COLD
(即需要開啟冷熱分離的表不能是冷存表)。如何修改儲存屬性,請參見配置冷儲存。
操作步驟
方式一:HBase Shell
建立表並設定冷熱分界線。
HBase(main):002:0> create 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'86400'}
參數說明
NAME:需要冷熱分離的列簇。
COLD_BOUNDARY:冷熱分離時間點,單位為秒(s)。例如COLD_BOUNDARY為86400,代表86400秒(一天)前寫入的資料會被自動歸檔到冷儲存。
可選:修改冷熱分離分界線。
HBase(main):005:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'42300'}
可選:取消冷熱分離。
HBase(main):004:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>""}
說明修改冷熱分界線或取消冷熱分離後,需要等待系統後台執行完
compaction
,資料才能從冷儲存回到熱儲存中。如果需要資料立即回到熱儲存,請手動執行major_compact命令。
方式二:HBase Java API
建立表並設定冷熱分界線。
Admin admin = connection.getAdmin(); TableName tableName = TableName.valueOf("chsTable"); HTableDescriptor descriptor = new HTableDescriptor(tableName); HColumnDescriptor cf = new HColumnDescriptor("f"); cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "86400"); descriptor.addFamily(cf); admin.createTable(descriptor);
可選:修改冷熱分界線。
HTableDescriptor descriptor = admin .getTableDescriptor(tableName); HColumnDescriptor cf = descriptor.getFamily("f".getBytes()); cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "86400"); admin.modifyTable(tableName, descriptor);
可選:取消冷熱分離。
HTableDescriptor descriptor = admin .getTableDescriptor(tableName); HColumnDescriptor cf = descriptor.getFamily("f".getBytes()); cf.setValue(AliHBaseConstants.COLD_BOUNDARY, null); admin.modifyTable(tableName, descriptor);
說明修改冷熱分界線或取消冷熱分離後,需要等待系統後台執行完
compaction
後,資料才能從冷儲存回到熱儲存中。
方式三:lindorm-cli
設定表的冷熱分界線。
方法一:建表時開啟冷熱分離並設定冷熱分界線。
CREATE TABLE dt ( p1 integer, p2 integer, c1 varchar, c2 bigint, constraint pk primary key(p1 desc)) WITH (COMPRESSION = 'ZSTD', CHS = '86400', CHS_L2 = 'storagetype=COLD');
參數說明
CHS:冷熱分離時間點,單位為秒(s)。例如CHS為86400,代表自訂時間列的時間戳記取值在86400秒(一天)前的資料會被自動歸檔到冷儲存。
COMPRESSION:壓縮演算法。整張表生效。演算法名稱大小寫不敏感。預設配置為none。
CHS_L2:配置第二層屬性,一般配置儲存屬性:storagetype=COLD。
方法二:建表時未設定,通過
ALTER TABLE
語句添加相關屬性。-- 已有資料表且建表時未開啟冷熱分離,例如: -- CREATE TABLE dt (p1 integer, p2 integer, c1 varchar, c2 bigint, constraint pk primary key(p1 desc)) WITH (COMPRESSION = 'ZSTD'); -- 為表dt開啟冷熱分離功能 ALTER TABLE dt SET 'CHS' ='86400', 'CHS_L2' = 'storagetype=COLD';
可選:修改冷熱分界線。
ALTER TABLE dt SET 'CHS'='1000';
可選:取消冷熱分離。
ALTER TABLE dt SET 'CHS'='', 'CHS_L2' = '';
說明修改冷熱分界線或取消冷熱分離後,需要等待系統後台執行完成
compaction
,資料才能從冷儲存回到熱儲存中。如果需要資料立即回到熱儲存,請手動執行major_compact
。具體文法,請參見ALTER TABLE。
資料寫入
冷熱分離的表與普通表的資料寫入方式完全一致,時間戳記預設為資料寫入時的當前系統時間。您也可以在使用HBase介面寫入資料時自訂時間戳記。新寫入的資料會先儲存在熱儲存(標準型/效能型)中,隨著時間的推移,如果目前時間-寫入時間>COLD_BOUNDARY/CHS設定的值
,則會在系統後台執行compaction
時被歸檔到冷儲存中。
自訂時間戳記可能會影響資料的冷熱判斷。系統會將目前時間與自訂時間戳記進行比較,假設冷熱分離線為3天,自訂時間戳記為系統目前時間3天后的某一時間點,則寫入的資料會比當前寫入時間延遲3天才能進入冷儲存;假設冷熱分離時間點為3天,自訂時間戳記為3天前的某一時間點,則新寫入的資料將會通過非同步歸檔進入冷儲存。
資料查詢
由於冷熱資料都在同一張表中,因此所有的查詢操作都只需在一張表內進行。在查詢時,可以通過調整TimeRange參數來設定查詢的時間範圍,系統將會根據指定的時間範圍決定查詢方式,如僅查詢熱儲存區、僅查詢冷儲存區或同時查詢冷儲存區和熱儲存區。如果查詢時未指定時間範圍,系統可能會查詢到冷資料,進而使得查詢輸送量受到冷儲存的限制。詳細內容,請參見冷儲存介紹。
如果已確定需要查詢的資料為熱資料,也可以使用HINT設定_l_hot_only_或HOT_ONLY參數的屬性來避免查詢冷資料。
冷熱分離表中的冷儲存僅用于歸檔資料,查詢請求很少。如果有大量請求需要去查詢冷資料,則需要考慮當前COLD_BOUNDARY設定的值是否會導致系統查詢的冷資料過多進而影響查詢效率。
如果一行資料已經在冷儲存,但這行資料後續有更新,更新的欄位會先儲存在熱儲存。如果此時設定HOT_ONLY或TimeRange只查詢熱儲存中的資料,則只會返回這一行儲存在熱儲存的欄位。只有在查詢時去掉HOT_ONLY和TimeRange,或保證TimeRange覆蓋了該行資料的插入和更新時間,才能完整返回這一行資料。因此不建議更新進入冷儲存區的資料。
目前支援隨機查詢Get和範圍查詢Scan兩種查詢方式。
隨機查詢Get
方式一:HBase Shell
不帶HOT_ONLY的查詢,可能會查詢到冷資料。
HBase(main):013:0> get 'chsTable', 'row1'
帶HOT_ONLY的查詢,只查詢熱資料。
HBase(main):015:0> get 'chsTable', 'row1', {HOT_ONLY=>true}
帶TimeRange的查詢,系統會通過比較TimeRange與COLD_BOUNDARY來決定查詢方式,即僅查詢冷資料、僅查詢熱資料或冷熱資料都查詢。
HBase(main):016:0> get 'chsTable', 'row1', {TIMERANGE => [0, 1568203111265]}
參數說明
TimeRange:時間範圍。格式為Unix時間戳記,單位為毫秒(ms)。
方式二:HBase Java API
不帶HOT_ONLY的查詢,可能會查詢到冷資料。
Get get = new Get("row1".getBytes()); System.out.println("result: " + table.get(get));
帶HOT_ONLY的查詢,只查詢熱資料。
get = new Get("row1".getBytes()); get.setAttribute(AliHBaseConstants.HOT_ONLY, Bytes.toBytes(true));
帶TimeRange的查詢,系統會根據設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料。
get = new Get("row1".getBytes()); get.setTimeRange(0, 1568203111265)
方式三:Lindorm SQL
SELECT /*+ _l_hot_only_ */ * FROM dt WHERE pk IN (1, 2, 3);
範圍查詢Scan
如果範圍查詢(Scan)不設定HOT_ONLY參數和TimeRange,或TimeRange包含位於冷儲存區的時間,則系統會並行查詢冷熱資料併合並結果。
範圍查詢僅支援HBase Shell和HBase Java API使用方式。
方式一:HBase Shell
不帶HOT_ONLY的查詢,一定會查詢到冷資料。
Lindorm(main):017:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9'}
帶HOT_ONLY的查詢,只查詢熱資料。
Lindorm(main):018:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', HOT_ONLY=>true}
帶TimeRange的查詢,系統會將設定的TimeRange與COLD_BOUNDARY冷熱分界線進行比較來決定查詢哪個地區的資料。
Lindorm(main):019:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', TIMERANGE => [0, 1568203111265]}
方式二:HBase Java API
TableName tableName = TableName.valueOf("chsTable"); Table table = connection.getTable(tableName); // 不帶Hot_Only的查詢,一定會查詢到冷資料 Scan scan = new Scan(); ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println("scan result:" + result); } // 帶Hot_Only的查詢,只會查詢熱資料部分 scan = new Scan(); scan.setAttribute(AliLindormConstants.HOT_ONLY, Bytes.toBytes(true)); // 帶TimeRange的查詢,系統會通過比較TimeRange與COLD_BOUNDARY來決定查詢方式 scan = new Scan(); scan.setTimeRange(0, 1568203111265);
優先查詢熱資料
在Scan情境下,查詢的資料可能既有冷資料又有熱資料,例如查詢一個使用者的所有訂單、聊天記錄等。查詢的結果通常以分頁的形式從新到舊展示,最先展示的往往是最近的熱資料。在此情境下,普通不帶Hot_Only的Scan會並行掃描冷熱資料,導致請求效能下降。而在開啟了優先查詢熱資料後,會優先只查熱資料。只有熱資料的數量未達到指定的Limit數量時,系統才會去查詢冷資料。減少冷儲存的訪問,可提升請求響應。
優先查詢熱資料功能僅支援HBase Shell和HBase Java API使用方式,不支援Lindorm SQL。
方式一:HBase Shell
Lindorm(main):002:0> scan 'chsTable', {COLD_HOT_MERGE=>true}
參數說明
COLD_HOT_MERGE:是否開啟優先查詢熱資料。
true:是。優先查詢熱儲存中的資料,若熱儲存中的資料查完了,使用者仍然在調用擷取下一條資料,則會開始查詢冷資料。
false:否。不開啟優先查詢熱資料功能。
方式二:HBase Java API
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排序,結果如下HBase Shell樣本所示。在實際使用時, 您可以通過Rowkey設計,保障Scan的結果仍然保序,比如訂單記錄表,Rowkey為使用者ID和訂單建立時間,掃描某個使用者的訂單資料是有序的。
//假設Rowkey為"coldRow"的這一行是冷資料,Rowkey為"hotRow"的這一行為熱資料 //正常情況下,由於Lindorm的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)
常見問題
Q:冷資料更新後還是冷資料嗎?
A:不是。由於更新後的資料重新記錄了時間戳記,因此冷資料更新後變為熱資料。
Q:我只想查詢熱資料,為什麼還返回了冷資料?
A:查詢語句可以通過設定HOT_ONLY/_l_hot_only_僅查詢熱資料。但由於資料歸檔至冷儲存的操作是周期性觸發的,因此有部分冷資料可能會滯留在熱儲存,導致查詢結果中包含了冷資料。可以在查詢條件中添加熱資料時間範圍,避免出現此類問題。樣本如下:
// 您需要設定_l_ts_min_(目前時間-冷熱分界線), _l_ts_max_(目前時間), 注意時間單位統一 SELECT /*+ _l_hot_only_(true), _l_ts_min_(1000), _l_ts_max_(2001) */ * FROM test WHERE p1>1;
Q:已設定時間範圍,也使用了HOT_ONLY,為什麼查詢熱資料逾時了?
A:這種情況一般發生在資料移轉或者修改表屬性為冷熱分離表後。此時系統尚未觸發資料歸檔至冷儲存,因此大量的冷資料滯留於熱儲存中,資料量的增大導致了查詢效率的降低。需要對錶執行
major compaction
操作來解決這一問題。具體文法,請參見ALTER TABLE。