HBaseクライアントを作る際のページング処理について
HBaseにデータをいっぱい突っ込んでHBaseクライアント作ってさーがりがり検索するぞーっていう場合に注意する点があります。
row keyで検索するとして単純に考えるとGetオブジェクを作って以下のように検索するでしょう。
Get get = new Get(rowkey); get.addFamily(columnFamily); Result result = table.get(get); List<KeyValue> list = result.list(); for (KeyValue keyValue : list) { System.out.println(Bytes.toString(keyValue.getValue())); }
こうすると1行に大量の列がある場合にResult#listでメモリ不足になる危険性があります。
じゃあ、どうするか?まあページング処理が必要ですよね。指定した列数だけ取得するためにはScan#setBatchを使います。
具体例をあげます。まずは以下のスクリプトを用意してHBaseにテスト用のテーブルを作成します。
create 'testtable', 'cf' 10.times { |i| put 'testtable', 'r1', "cf:q#{i}", "value#{i}" } 10.times { |i| put 'testtable', 'r2', "cf:q#{i}", "value#{i}" }
scan 'testtable' すると
r1 column=cf:q0, timestamp=1357640105830, value=value0 r1 column=cf:q1, timestamp=1357640105848, value=value1 r1 column=cf:q2, timestamp=1357640105863, value=value2 r1 column=cf:q3, timestamp=1357640105866, value=value3 r1 column=cf:q4, timestamp=1357640105869, value=value4 r1 column=cf:q5, timestamp=1357640105871, value=value5 r1 column=cf:q6, timestamp=1357640105873, value=value6 r1 column=cf:q7, timestamp=1357640105876, value=value7 r1 column=cf:q8, timestamp=1357640105878, value=value8 r1 column=cf:q9, timestamp=1357640105884, value=value9 r2 column=cf:q0, timestamp=1357640105929, value=value0 r2 column=cf:q1, timestamp=1357640105931, value=value1 r2 column=cf:q2, timestamp=1357640105934, value=value2 r2 column=cf:q3, timestamp=1357640105937, value=value3 r2 column=cf:q4, timestamp=1357640105941, value=value4 r2 column=cf:q5, timestamp=1357640105945, value=value5 r2 column=cf:q6, timestamp=1357640105949, value=value6 r2 column=cf:q7, timestamp=1357640105952, value=value7 r2 column=cf:q8, timestamp=1357640105954, value=value8 r2 column=cf:q9, timestamp=1357640105957, value=value9 2 row(s) in 0.4520 seconds
となります。
20行あるように見えますが、見方としては2行(r1とr2)あって各行が10列(cf:q0〜cf:q9)あります。
Scan#setBatchはこんな感じで使います。
Scan scan = new Scan(Bytes.toBytes("r1")); scan.addFamily(Bytes.toBytes("cf")); scan.setBatch(5); ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { List<KeyValue> list = result.list(); for (KeyValue keyValue : list) { System.out.println(Bytes.toString(keyValue.getRow()) + " " + Bytes.toString(keyValue.getValue())); } } scanner.close();
実行結果
r1 value0 r1 value1 r1 value2 r1 value3 r1 value4 r1 value5 r1 value6 r1 value7 r1 value8 r1 value9 r2 value0 r2 value1 r2 value2 r2 value3 r2 value4 r2 value5 r2 value6 r2 value7 r2 value8 r2 value9
5つづつ進むので一番外側のループは20/5=4回ループします。
Scanのコンストラクタには開始行しか渡していないのでr2まで含んだ結果がかえってきます。
Scan scan = new Scan(Bytes.toBytes("r1"), Bytes.toBytes("r1"));
として終了行も指定すればr1の結果のみ取得できます。
Scan(Get get)を使っても同様の結果になります。
GetとScanを使った場合のサンプルの完全版はこちら
import java.util.List; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes; public class GetScanSample { public static void main(String[] args) throws Throwable { Configuration conf = HBaseConfiguration.create(); String tableName = "testtable"; HTable table = new HTable(conf, tableName); System.out.println("Get start"); Get get = new Get(Bytes.toBytes("r1")); get.addFamily(Bytes.toBytes("cf")); Result result1 = table.get(get); List<KeyValue> list1 = result1.list(); for (KeyValue keyValue : list1) { System.out.println(Bytes.toString(keyValue.getRow()) + " " + Bytes.toString(keyValue.getValue())); } System.out.println("Get end"); System.out.println("Scan start"); Scan scan = new Scan(get); int limit = 5; int offset = 0; scan.setBatch(limit); ResultScanner scanner = table.getScanner(scan); int count = 0; for (Result result : scanner) { if(count == offset) { List<KeyValue> list = result.list(); for (KeyValue keyValue : list) { System.out.println(Bytes.toString(keyValue.getRow()) + " " + Bytes.toString(keyValue.getValue())); } break; } count++; } scanner.close(); System.out.println("Scan end"); } }
実行結果
Get start r1 value0 r1 value1 r1 value2 r1 value3 r1 value4 r1 value5 r1 value6 r1 value7 r1 value8 r1 value9 Get end Scan start r1 value0 r1 value1 r1 value2 r1 value3 r1 value4 Scan end
offsetを1にすれば後半のvalue5〜value9を取得できますのでページング処理はこんな感じでできます。
この辺は馬本の3.5 スキャンに書かれているので読むと良いでしょう。
列に対してのフェッチ数はsetBatchで指定し行に対してのフェッチ数はsetCachingで指定します。
いじょ