LindormTable は、データの保存期間に基づいて、ホットストレージとコールドストレージの間でデータを自動的にルーティングします。テーブルまたはカラムファミリーに時間境界を設定すると、その境界より新しいデータはホットストレージに保持され、古いデータはコンパクション中にコールドストレージにアーカイブされます。
仕組み
データがテーブルに書き込まれると、まずホットストレージ (Standard または Performance タイプ) に格納されます。Lindorm はバックグラウンドで定期的に コンパクション 操作を実行します。コンパクション中、Lindorm は各行のタイムスタンプを設定された境界 (HBase Shell と Java API の場合は COLD_BOUNDARY、Lindorm SQL の場合は CHS) と比較します。タイムスタンプが境界外の行は、コールドストレージに移動されます。
アーカイブは非同期で行われます。つまり、データは境界を過ぎた瞬間に移動されるわけではありません。即時転送を強制するには、手動で major_compact を実行してください。
デフォルトでは、行のタイムスタンプはデータが書き込まれたときのシステム時刻です。ApsaraDB for HBase API for Java を使用する場合、カスタムタイムスタンプを指定できます。カスタムタイムスタンプがストレージのルーティングにどのように影響するかについては、「運用上の考慮事項」をご参照ください。
前提条件
開始する前に、以下を確認してください:
コールドストレージ機能が有効化されていること。機能の有効化方法の詳細については、「容量ストレージの有効化」をご参照ください。
LindormTable のバージョンが 2.1.8 以降であること。詳細については、「LindormTable のリリースノート」および「Lindorm インスタンスのマイナーエンジンバージョンのアップグレード」をご参照ください。
次のいずれかを使用して LindormTable への接続が確立されていること:
Apache HBase Shell。詳細については、「Lindorm Shell を使用した LindormTable への接続」をご参照ください。
ApsaraDB for HBase API for Java。詳細については、「ApsaraDB for HBase API for Java を使用したアプリケーション開発」をご参照ください。
lindorm-cli。詳細については、「lindorm-cli を使用した LindormTable への接続と利用」をご参照ください。
ターゲットのテーブルまたはカラムファミリーの
STORAGE_POLICY属性がCOLD以外の値に設定されていること。詳細については、「コールドストレージの設定」をご参照ください。
ホット/コールドデータ境界の設定
お使いのクライアントに合った方法を選択してください。
Apache HBase Shell の使用
手順1:境界を設定してテーブルを作成
HBase(main):002:0> create 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'86400'}| パラメーター | 説明 |
|---|---|
NAME | 設定対象のカラムファミリー。 |
COLD_BOUNDARY | 境界を秒単位で指定します。この値より長く保存されたデータは、コンパクション中にコールドストレージにアーカイブされます。たとえば、86400 は 1 日に相当します。ホット/コールドデータ分離を無効にするには、"" (空の文字列) に設定してください。 |
手順2 (任意):境界の変更
境界を変更すると、次回のコンパクション時にデータがコールドストレージとホットストレージの間で移動します。即時転送をトリガーするには、major_compact 'chsTable' を実行してください。
HBase(main):005:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>'42300'}手順3 (任意):ホット/コールドデータ分離の無効化
ホット/コールドデータ分離を無効にすると、次回のコンパクション時にデータがコールドストレージからホットストレージに戻されます。即時転送をトリガーするには、major_compact 'chsTable' を実行してください。
HBase(main):004:0> alter 'chsTable', {NAME=>'f', COLD_BOUNDARY=>""}ApsaraDB for HBase API for Java の使用
手順1:境界を設定してテーブルを作成
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);手順2 (任意):境界の変更
境界を変更すると、次回のコンパクション時にデータがコールドストレージとホットストレージの間で移動します。
HTableDescriptor descriptor = admin
.getTableDescriptor(tableName);
HColumnDescriptor cf = descriptor.getFamily("f".getBytes());
cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "86400");
admin.modifyTable(tableName, descriptor);手順3 (任意):ホット/コールドデータ分離の無効化
ホット/コールドデータ分離を無効にすると、次回のコンパクション時にデータがコールドストレージからホットストレージに戻されます。
HTableDescriptor descriptor = admin
.getTableDescriptor(tableName);
HColumnDescriptor cf = descriptor.getFamily("f".getBytes());
cf.setValue(AliHBaseConstants.COLD_BOUNDARY, "");
admin.modifyTable(tableName, descriptor);lindorm-cli (Lindorm SQL) の使用
手順1:境界を設定してテーブルを作成
CREATE TABLE ステートメントを使用します:
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');または、ALTER TABLE を使用して既存のテーブルでホット/コールドデータ分離を有効にします:
-- 既存のテーブルでホット/コールドデータ分離を有効化します。
-- テーブルは CHS なしで作成されました:
-- CREATE TABLE dt (p1 integer, p2 integer, c1 varchar, c2 bigint,
-- constraint pk primary key(p1 desc)) WITH (COMPRESSION = 'ZSTD');
ALTER TABLE dt SET 'CHS' ='86400', 'CHS_L2' = 'storagetype=COLD';| パラメーター | 説明 |
|---|---|
CHS | 境界を秒単位で指定します。この値より長く保存されたデータは、コンパクション中にコールドストレージにアーカイブされます。たとえば、86400 は 1 日に相当します。ホット/コールドデータ分離を無効にするには、'' (空の文字列) に設定してください。 |
CHS_L2 | レイヤー 2 のストレージ属性です。storagetype=COLD に設定します。 |
COMPRESSION | テーブル内のすべてのデータに適用される圧縮アルゴリズムです。値は大文字と小文字を区別しません。デフォルト値: NONE。 |
手順2 (任意):境界の変更
境界を変更すると、次回のコンパクション時にデータがコールドストレージとホットストレージの間で移動します。即時転送をトリガーするには、テーブルで major_compact を実行してください。構文の詳細については、「ALTER TABLE」をご参照ください。
ALTER TABLE dt SET 'CHS'='1000';手順3 (任意):ホット/コールドデータ分離の無効化
ホット/コールドデータ分離を無効にすると、次回のコンパクション時にデータがコールドストレージからホットストレージに戻されます。即時転送をトリガーするには、テーブルで major_compact を実行してください。
ALTER TABLE dt SET 'CHS'='', 'CHS_L2' = '';データの書き込み
ホット/コールドデータが分離されたテーブルへのデータの書き込みは、標準のワイドテーブルへの書き込みと同じ方法で行います。データは常に最初にホットストレージに書き込まれ、設定された境界を超えると次回のコンパクションでコールドストレージに移動されます。
ApsaraDB for HBase API for Java を使用する場合、各行にカスタムタイムスタンプを設定できます。行がどのストレージ階層に配置されるかは、書き込み操作自体の時刻ではなく、このタイムスタンプによって決まります。詳細については、「カスタムタイムスタンプの動作」をご参照ください。
データのクエリ
LindormTable はホットデータとコールドデータを同じテーブルに保存するため、すべてのデータに単一のクエリでアクセスできます。デフォルトでは、時間範囲を指定しないクエリはコールドストレージを読み取る可能性があり、その場合、スループットはコールドストレージの仕様によって制限されます。
クエリをホットストレージに限定するには、次のいずれかを使用してください:
HOT_ONLYパラメーター (Apache HBase Shell および Java API)_l_hot_only_ヒント (Lindorm SQL)TimeRangeパラメーター (ホットデータの期間内に収まる範囲に設定)
コールドストレージの行にあるフィールドが更新されると、更新されたフィールドはホットストレージに保存され、元のデータはコールドストレージに残ります。HOT_ONLY または狭い TimeRange を使用してクエリを実行すると、行全体ではなく、更新されたフィールドのみが返されます。完全な行を返すには、HOT_ONLY を使用せずにクエリを実行し、TimeRange が最初の挿入から最新の更新までの時間をカバーするようにしてください。コールドストレージに保存されているデータの更新は避けることを推奨します。
GET クエリ
Apache HBase Shell
-- すべてのデータをクエリ (コールドストレージを含む場合があります)
HBase(main):013:0> get 'chsTable', 'row1'
-- ホットストレージのみをクエリ
HBase(main):015:0> get 'chsTable', 'row1', {HOT_ONLY=>true}
-- 時間範囲でクエリ (UNIX タイムスタンプ、ミリ秒単位)
HBase(main):016:0> get 'chsTable', 'row1', {TIMERANGE => [0, 1568203111265]}TimeRange パラメーターは、1970 年 1 月 1 日 00:00:00 UTC からの UNIX タイムスタンプをミリ秒単位で受け入れます。Lindorm は TimeRange を COLD_BOUNDARY と比較して、どのストレージ階層を読み取るかを決定します。
ApsaraDB for HBase API for Java
// すべてのデータをクエリ (コールドストレージを含む場合があります)
Get get = new Get("row1".getBytes());
System.out.println("result: " + table.get(get));
// ホットストレージのみをクエリ
get = new Get("row1".getBytes());
get.setAttribute(AliHBaseConstants.HOT_ONLY, Bytes.toBytes(true));
// 時間範囲でクエリ
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 クエリをサポートしているのは、Apache HBase Shell と ApsaraDB for HBase API for Java のみです。HOT_ONLY またはホットデータのみをカバーする TimeRange を指定しない場合、Lindorm は両方のストレージ階層を読み取り、結果をマージします。
Apache HBase Shell
-- すべてのデータをスキャン (コールドストレージを含む場合があります)
Lindorm(main):017:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9'}
-- ホットストレージのみをスキャン
Lindorm(main):018:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', HOT_ONLY=>true}
-- 時間範囲でスキャン
Lindorm(main):019:0> scan 'chsTable', {STARTROW =>'row1', STOPROW=>'row9', TIMERANGE => [0, 1568203111265]}ApsaraDB for HBase API for Java
TableName tableName = TableName.valueOf("chsTable");
Table table = connection.getTable(tableName);
// すべてのデータをスキャン (コールドストレージを含む場合があります)
Scan scan = new Scan();
ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {
System.out.println("scan result:" + result);
}
// ホットストレージのみをスキャン
scan = new Scan();
scan.setAttribute(AliHBaseConstants.HOT_ONLY, Bytes.toBytes(true));
// 時間範囲でスキャン
scan = new Scan();
scan.setTimeRange(0, 1568203111265);SCAN クエリにおけるホットデータの優先
ユーザーのすべての注文やメッセージなど、順序付けられたデータを取得する SCAN クエリでは、LindormTable は通常、ホットストレージとコールドストレージの両方を読み取るため、応答時間が増加します。COLD_HOT_MERGE を使用してホットデータの優先を有効にすると、最初にホットストレージがスキャンされます。コールドストレージは、ホットストレージ内の行数がクエリで取得する最小行数より少ない場合にのみ読み取られます。
COLD_HOT_MERGE は、Apache HBase Shell と ApsaraDB for HBase API for Java でのみ利用可能です。
Apache HBase Shell
Lindorm(main):002:0> scan 'chsTable', {COLD_HOT_MERGE=>true}| パラメーター | 値 | 説明 |
|---|---|---|
COLD_HOT_MERGE | true | 最初にホットストレージをスキャンします。コールドストレージは、ホットストレージ内の行数がクエリで取得する最小行数より少ない場合にのみ読み取られます。 |
COLD_HOT_MERGE | false | ホットデータの優先を無効にします。 |
ApsaraDB for HBase API for Java
scan = new Scan();
scan.setAttribute(AliHBaseConstants.COLD_HOT_MERGE, Bytes.toBytes(true));
scanner = table.getScanner(scan);使用上の注意
行キーの順序は保証されません。
COLD_HOT_MERGEが有効な場合、ホットデータ行がコールドデータ行の前に返されます。結果セットには、ホットデータ行とコールドデータ行がそれぞれ行キーで個別にソートされて含まれますが、全体の結果は行キーでグローバルにソートされていません。// 通常のスキャン (辞書順) 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=true の場合 (ホットデータ行が先) 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)アプリケーションで意味のある順序を維持するには、顧客 ID と注文作成時間などの複合キーのように、順序情報をエンコードする行キーを設計してください。
更新されたフィールドを持つ行が 2 回表示されます。コールドストレージ内の行が部分的に更新された場合、その行はホットストレージとコールドストレージの両方にまたがります。
COLD_HOT_MERGEが有効な場合、結果には同じ行キーに対して 2 つのエントリ (1 つはホットストレージから、もう 1 つはコールドストレージから) が含まれます。
運用上の考慮事項
カスタムタイムスタンプの動作
行に設定したタイムスタンプは、書き込み時刻ではなく、その行がどのストレージ階層に配置されるかを決定します。例:
3 日後のタイムスタンプで書き込まれた行は、現在のタイムスタンプで書き込まれた行よりも 3 日遅れてコールドストレージにアーカイブされます。
境界が 3 日に設定されている場合、3 日前のタイムスタンプで書き込まれた行は、さらに 3 日後ではなく、書き込み直後に非同期でコールドストレージにアーカイブされます。
カスタムタイムスタンプは慎重に使用してください。過去のタイムスタンプで書き込まれた行は、次回のコンパクション直後にコールドストレージに移動する可能性があり、最近書き込まれたデータが常にホットストレージにあると想定するアクセスパターンでは予期しない動作となることがあります。
頻繁にアクセスされるコールドデータ
クエリが一貫してコールドストレージにヒットする場合、COLD_BOUNDARY (または CHS) の値が実際のアクセスパターンと一致しているか確認してください。コールドストレージのスループットはコールドストレージの仕様によって制限されるため、頻繁にアクセスされるコールドデータはクエリのパフォーマンスを低下させます。
よくある質問
コールドデータを更新した後、そのデータはコールドデータのままですか?
いいえ。行を更新するとタイムスタンプが更新されるため、LindormTable はそれをホットデータとして扱います。
HOT_ONLY を設定しましたが、クエリがまだコールドデータを返します。なぜですか?
データはコンパクション中に非同期でコールドストレージにアーカイブされます。コールドデータになるほど古い一部の行はまだアーカイブされていない可能性があり、そのため物理的にはまだホットストレージにあり、クエリによって返されます。
このデータを除外するには、HOT_ONLY に加えて明示的な時間範囲を指定してください。Lindorm SQL で _l_ts_min_ および _l_ts_max_ ヒントを使用して、期間を正確に定義してください:
-- _l_ts_min_: 現在時刻とホット/コールドデータ境界との差
-- _l_ts_max_: 現在のシステム時刻
-- 両方の値は同じ単位を使用する必要があります。
SELECT /*+ _l_hot_only_(true), _l_ts_min_(1000), _l_ts_max_(2001) */ * FROM test WHERE p1>1;HOT_ONLY と TimeRange を指定したにもかかわらず、クエリがタイムアウトします。なぜですか?
これは通常、テーブルにデータを移行した後、または初めてホット/コールドデータ分離を有効にした後に発生します。コンパクションがまだ実行されていないため、大量のコールドデータが物理的にホットストレージに残っています。テーブルで major_compact を実行してデータを強制的に移動させてください。構文の詳細については、「ALTER TABLE」をご参照ください。