HBase 入門 (1)
Hadoop と HBase で分散処理が書けるのはわかったけど、いざ使ってみるとなるとドキュメントが少なくて……、というあなたの為の HBase 入門、今回はセットアップから Java の API で HBase を触るまでを勉強します。
使用するバージョンは Hadoop 0.17.2.1、HBase 0.2.1 です。
0. 参考ページ
http://hadoop.apache.org/core/docs/r0.17.2/
FrontPage - Hadoop Wiki
http://hadoop.apache.org/core/docs/r0.17.2/api/index.html
Hbase - Hadoop Wiki
http://hadoop.apache.org/hbase/docs/r0.2.1/api/index.html
1. Hadoop のセットアップ
http://hadoop.apache.org/core/docs/r0.17.2/quickstart.html を参考にインストールします。Java 1.5.x とありますが、1.6 でもたぶん動きます。今回は学習用にスタンドアローンで動かすので、$HADOOP_HOME/conf/hadoop-env.sh の JAVA_HOME を書くだけでよいでしょう。サンプルで動作を確認しましょう。
2. HBase のセットアップ
ダウンロードページ からとってきて展開します。Hadoop と同じく、$HBASE_HOME/conf/hadoop-env.sh の JAVA_HOME だけ設定します。
3. HBase の起動
$HBASE_HOME/bin/start-hbase.sh を実行して HBase を起動します。
debian:~/lib/hbase-0.2.1% bin/start-hbase.sh starting master, logging to /home/wanpark/lib/hbase-0.2.1/bin/../logs/hbase-wanpark-master-debian.out localhost: starting regionserver, logging to /home/wanpark/lib/hbase-0.2.1/bin/../logs/hbase-wanpark-regionserver-debian.out
Hadoop の起動は不要です。
4. shell で HBase に慣れよう
$HBASE_HOME/bin/hbase shell でシェルが起動します。実体は irb です。いくつかコマンドを打って慣れましょう。
debian:~/lib/hbase-0.2.1% bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Version: 0.2.1, r693938, Wed Sep 10 13:13:09 PDT 2008 hbase(main):001:0> help HBASE SHELL COMMANDS: alter Alter column family schema; pass table name and a dictionary ...
次のような成績表を作りましょう(http://www.nabble.com/Re%3A-Map-Reduce-over-HBase---sample-code-p18253120.html を参考にしました)。
grade: | course:math | course:art | |
---|---|---|---|
Dan | 1 | 87 | 97 |
Dana | 2 | 100 | 80 |
縦を column、横を row、データの1マス1マスを cell と呼びます。
hbase(main):002:0> create 'scores', 'grade', 'course' 0 row(s) in 4.1610 seconds hbase(main):003:0> list scores 1 row(s) in 0.0210 seconds hbase(main):004:0> describe 'scores' {NAME => 'scores', IS_ROOT => 'false', IS_META => 'false', FAMILIES => [{NAME => 'course', BLOOMFILTER => 'fal se', IN_MEMORY => 'false', LENGTH => '2147483647', BLOCKCACHE => 'false', VERSIONS => '3', TTL => '-1', COMPRE SSION => 'NONE'}, {NAME => 'grade', BLOOMFILTER => 'false', IN_MEMORY => 'false', LENGTH => '2147483647', BLOC KCACHE => 'false', VERSIONS => '3', TTL => '-1', COMPRESSION => 'NONE'}]} 1 row(s) in 0.0130 seconds hbase(main):005:0> put 'scores', 'Dan', 'grade:', '1' 0 row(s) in 0.0070 seconds hbase(main):006:0> put 'scores', 'Dan', 'course:math', '87' 0 row(s) in 0.0040 seconds hbase(main):007:0> put 'scores', 'Dan', 'course:art', '97' 0 row(s) in 0.0030 seconds hbase(main):008:0> put 'scores', 'Dana', 'grade:', '2' 0 row(s) in 0.0040 seconds hbase(main):009:0> put 'scores', 'Dana', 'course:math', '100' 0 row(s) in 0.0030 seconds hbase(main):010:0> put 'scores', 'Dana', 'course:art', '80' 0 row(s) in 0.0050 seconds hbase(main):011:0> get 'scores', 'Dan' COLUMN CELL course:art timestamp=1224726394286, value=97 course:math timestamp=1224726377027, value=87 grade: timestamp=1224726360727, value=1 3 row(s) in 0.0070 seconds hbase(main):012:0> scan 'scores' ROW COLUMN+CELL Dan column=course:art, timestamp=1224726394286, value=97 Dan column=course:math, timestamp=1224726377027, value=87 Dan column=grade:, timestamp=1224726360727, value=1 Dana column=course:art, timestamp=1224726424967, value=80 Dana column=course:math, timestamp=1224726416145, value=100 Dana column=grade:, timestamp=1224726404965, value=2 6 row(s) in 0.0410 seconds hbase(main):013:0> scan 'scores', ['course:'] ROW COLUMN+CELL Dan column=course:art, timestamp=1224726394286, value=97 Dan column=course:math, timestamp=1224726377027, value=87 Dana column=course:art, timestamp=1224726424967, value=80 Dana column=course:math, timestamp=1224726416145, value=100 4 row(s) in 0.0200 seconds
MySQL などの一般的な RDB では row 単位でデータを扱いますが、HBase では cell がデータの単位です。
カラム名は (ファミリー名):(サブカラム名) となっていて、テーブルの定義ではファミリー名のみを指定します。サブカラム名はデータを挿入する際に自由に付けることができます。上の scan の例のように、同一ファミリーのカラムはまとめて指定できます。
各 cell にはエポックミリ秒の timestamp がついています。同一の row, column でもtimestamp が異なれば複数の cell が保存できます。ですが基本的に最新の timestamp を持つ cell の使用のみが想定されているようなので、同一 column 名での保存は上書きに近いです。
5. Java から HBase を使おう
Hadoop/HBase の jar と conf、それと必要なライブラリに classpath を通しておきます。ant などを使う場合は $HADOOP_HOME/lib 以下の jar ファイルをクラスパスに加えるといいでしょう。
debian:~/tmp/hbase% export HADOOP_HOME=~/lib/hadoop-0.17.2.1 debian:~/tmp/hbase% export HBASE_HOME=~/lib/hbase-0.2.1 debian:~/tmp/hbase% export CLASSPATH=.:$HADOOP_HOME/hadoop-0.17.2.1-core.jar:$HADOOP_HOME/conf:$HBASE_HOME/hbase-0.2.1.jar:$HBASE_HOME/conf:$HADOOP_HOME/lib/commons-logging-1.0.4.jar:$HADOOP_HOME/lib/log4j-1.2.13.jar
前項と同じ成績表を作ります。
import java.io.IOException; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.util.Map; import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.io.BatchUpdate; import org.apache.hadoop.hbase.io.RowResult; import org.apache.hadoop.hbase.io.Cell; import org.apache.hadoop.hbase.util.Writables; public class HBaseBasic { public static void main(String[] args) throws Exception { HBaseConfiguration config = new HBaseConfiguration(); HBaseAdmin admin = new HBaseAdmin(config); if (admin.tableExists("scores")) { System.out.println("drop table"); admin.disableTable("scores"); admin.deleteTable("scores"); } System.out.println("create table"); HTableDescriptor tableDescripter = new HTableDescriptor("scores".getBytes()); tableDescripter.addFamily(new HColumnDescriptor("grade:")); tableDescripter.addFamily(new HColumnDescriptor("course:")); admin.createTable(tableDescripter); HTable table = new HTable(config, "scores"); System.out.println("add Dan's data"); BatchUpdate danUpdate = new BatchUpdate("Dan"); danUpdate.put("grade:", Writables.getBytes(new IntWritable(1))); danUpdate.put("course:math", Writables.getBytes(new IntWritable(87))); danUpdate.put("course:art", Writables.getBytes(new IntWritable(97))); table.commit(danUpdate); System.out.println("add Dana's data"); BatchUpdate danaUpdate = new BatchUpdate("Dana"); danaUpdate.put("grade:", Writables.getBytes(new IntWritable(2))); danaUpdate.put("course:math", Writables.getBytes(new IntWritable(100))); danaUpdate.put("course:art", Writables.getBytes(new IntWritable(80))); table.commit(danaUpdate); for (RowResult row : table.getScanner(new String[] { "course:" })) { System.out.format("ROW\t%s\n", new String(row.getRow())); for (Map.Entry<byte[], Cell> entry : row.entrySet()) { String column = new String(entry.getKey()); Cell cell = entry.getValue(); IntWritable value = new IntWritable(); Writables.copyWritable(cell.getValue(), value); System.out.format(" COLUMN\t%s\t%d\n", column, value.get()); } } } }
HBase には byte 列でアクセスするのでややまどろっこしい事になっていますが、やっていることは shell の時と同じです。これを実行します。
debian:~/tmp/hbase% javac HBaseBasic.java debian:~/tmp/hbase% java HBaseBasic drop table 08/10/23 12:51:59 INFO client.HBaseAdmin: Disabled scores 08/10/23 12:51:59 INFO client.HBaseAdmin: Deleted scores create table add Dan's data add Dana's data ROW Dan COLUMN course:art 97 COLUMN course:math 87 ROW Dana COLUMN course:art 80 COLUMN course:math 100
今日の日記
かまってほしくてanondに機能つけたよーと書いたらちょっとうざがられた。http://wiredvision.jp/news/200801/2008012520.htmlを見て、合成したDNAを酵母の中で繋げて今までより1オーダー長いのが作れましたってこと?ともかくDNAプログラマーなんて楽しそうだねーと思った。えーと後は、水曜に雪が降ったのでざくっざくざくと歌いながら散歩をしていたら迷子になってしまって携帯電話も持ってないし困ったなーと途方に暮れていたらたまたま下校中の知り合いの小学生に会って道案内をしてもらって助かった。ひどい話だな。
Gears of War
ついに収容所を抜けた。そのままCasualをクリア。ジンルイダ。ドカーン。フィードのインポート元を見誤ってtumblrのきれい写真をやたらみてしまった。写真となるととたんに感傷的になるヤツラメ、クルシメー。
きょうの日記 プログラムを書くために理解が必要なレイヤーに限りはあるの?
論理的な面は一階述語論理を抑えておけばいいんだろう。ハードウェアがわからない。きっと技術が進歩したら素粒子はまたなんとか子からできていることがわかって、でそういう無限後退は折り畳みなんとか理論でどうこうなって、って感じかなーと思っていたんだけど、Physical Chemistry: A Molecular Approachを読んでいたらそうでもない気がしてきた。というのもさ、原子スケールの世界が単純すぎると思うんだよね。シュレーディンガー方程式を解くのもニュートンの運動方程式も、せいぜい自由度に整数と実数くらいの差がある程度でそんなに変わらなそうなのに。整数が相互作用してべき乗で実数でそのくらいか。いやよくわからない。ともかくおかしい。もっと素粒子がごちゃごちゃしてわけわからなくなってるものじゃないの?わかってないってだけかな。自分と違うものは画一的に見えるものだからね。先週妹の高校の文化祭に行って高校生達を見てたら、彼らは世界を漫画のストーリーとして認識してるんじゃないかと思ったからね。学校に行く前に妹の本棚の漫画を読んだからなんだけど。イキガミっていう中高生向け人間交差点みたいな話で、嫌なものを見たなと思った。また勉強したら更新します。しなそう。
きょうの日記
夜9時から夕方6時まで寝てしまった。大戸屋でチキンかあさん煮定食を食べる。寝起きで食べるには味が濃い。その後最近は興味が一周して生物学ブームなのでDevelopmental Biologyを読み進める。学部の発生の授業くらいはやっているので英語の感覚をとりもどそうというチョイスで。適当な総説を読むほうが楽しそうなんだけど手頃な入手手段がない。大学のそばに住みたいな。最終的な目的は生物様のシステムを作ろうで、発生様式をかたっぱしから知りたいなーと思っていたんだけど、脊椎動物や昆虫以外は細胞系譜固定でと書いてあるのでそんなに面白くない気もする。いや脊椎動物がどうこうっていつの時代のヨーロッパだという話だ。ホヤと脊椎動物でこうも違うなんて不思議だねーなんだけど明確な差があるわけじゃないんだろう、というのは本に書いてあったことだけど。そういうところの変化によりその後の形態進化の質が変わったのだ、なんて話だと楽しいね。魚の形を考えるという本を読む。ETVでも頻出の左ヒラメに右カレイの教育的正当性をぶつぶつとグループで言う、にしても教育のことは知らないからいいやということになった。それから仕事を始めて、気が乗らないのですぐやめる。時計の短針と長針を見違えてブックファーストに行きそびれた。アナログ時計のUIがうんたら、というのは今web日記用に考えたことで特に思ってはない。そういえばkusigahamaがなんか言ってたなと思ってTest Drive Unlimitedを買う。いまここ