セカンダリインデックス
次に、セカンダリインデックスについて説明します。
セカンダリインデックスを用いることで、対象カラムのソートが含まれるクエリやWHERE句に条件があるクエリが高速になる場合があります。ただし、データの追加・更新時にセカンダリインデックスを更新するため、多少のオーバヘッドがかかります。
Phoenixのセカンダリインデックスは以下の2種類があります。
- Mutable Indexing
- Immutable Indexing
それぞれについて説明していきます。
セカンダリインデックスを使うためには以下の設定が必要です。以下の設定をhbase-site.xmlに追加して、HBaseを再起動してください。
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
Mutable Indexing
Mutable Indexingは、一度作成したデータに対して変更をするようなユースケースに用います。Mutable Indexingでは、データの変更があった場合に、セカンダリインデックスに対してもその変更を反映します。
それでは、実際に使ってみましょう。まずは、Phoenixのコマンドラインを起動します。
$ cd <Phoenixのインストールディレクトリ>/bin $ ./sqlline.sh localhost
テーブルを作成します。
0: jdbc:phoenix:localhost> CREATE TABLE TBL1 ( . . . . . . . . . . . . .> COL1 VARCHAR NOT NULL PRIMARY KEY, . . . . . . . . . . . . .> COL2 VARCHAR, . . . . . . . . . . . . .> COL3 INTEGER . . . . . . . . . . . . .> );
次に、セカンダリインデックスを作成します。
以下では、INDEX1という名前で、COL2に対して昇順(デフォルト)のセカンダリインデックスを作成しています。
0: jdbc:phoenix:localhost> CREATE INDEX INDEX1 ON TBL1 (COL2);
ここで、実際にHBase上でどのようにセカンダリインデックスが表現されているか見てみましょう。
まずは、HBase shellを起動します。
$ <HBaseのインストールディレクトリ>/bin/hbase shell
HBase上でテーブルを確認してみます。
$ list TABLE INDEX1 SYSTEM.TABLE TBL1 3 row(s) in 0.0250 seconds
先ほど作成したセカンダリインデックスの名前で、HBase上にTableが作成されているのが分かります。Phoenixでは、セカンダリインデックスは実データのTableとは別のTableに保存されます。
それでは、実際にデータを入れてみます。
0: jdbc:phoenix:localhost> UPSERT INTO TBL1 VALUES ('aaa', '111', 1);
0: jdbc:phoenix:localhost> UPSERT INTO TBL1 VALUES ('bbb', '222',2);
0: jdbc:phoenix:localhost> UPSERT INTO TBL1 VALUES ('ccc', '333',3);
0: jdbc:phoenix:localhost> SELECT * FROM TBL1;
+------------+------------+------+
| COL1 | COL2 | COL3 |
+------------+------------+------+
| aaa | 111 | 1 |
| bbb | 222 | 2 |
| ccc | 333 | 3 |
+------------+------------+------+
HBase上の、セカンダリインデックスのテーブルの中身を見てみましょう。
$ scan 'INDEX1' ROW COLUMN+CELL 111\x00aaa column=_0:_0, timestamp=1392487975536, value= 222\x00bbb column=_0:_0, timestamp=1392487975643, value= 333\x00ccc column=_0:_0, timestamp=1392487975685, value= 3 row(s) in 0.1110 seconds
セカンダリインデックスのテーブルの中では、RowKeyにセカンダリインデックスを作成したCOL2の値と主キーであるCOL1の値がマッピングされているのが分かります。このセカンダリインデックスを使うことで、COL2の昇順ソートやWHERE句にCOL2の条件が含まれているクエリに対して高速に処理をすることが可能になります。
以下は、実際にセカンダリインデックスが使われている例です。EXPLAIN文で確認しています。
0: jdbc:phoenix:localhost> EXPLAIN SELECT COL1, COL2 FROM TBL1 WHERE COL2='222'; +------------+ | PLAN | +------------+ | CLIENT PARALLEL 1-WAY RANGE SCAN OVER INDEX1 ['222'] | +------------+
ここで、以下のクエリのEXPLAINしてみます。
0: jdbc:phoenix:localhost> EXPLAIN SELECT COL1, COL2, COL3 FROM TBL1 WHERE COL2='222'; +------------+ | PLAN | +------------+ | CLIENT PARALLEL 1-WAY FULL SCAN OVER TBL1 | | SERVER FILTER BY COL2 = '222' | +------------+
上記のクエリでは、COL2がWHERE句の条件に含まれているのにもかかわらず、セカンダリインデックスが使われていません。
Phoenixでは、現在のところ、セカンダリインデックスへのScanだけで解決できる場合のみ、セカンダリインデックスが使われるようです。
上記のクエリでは、COL3のデータを取得する必要がありますが、セカンダリインデックスにCOL3のデータが含まれていないので、セカンダリインデックスが使われませんでした。
これの解決方法として、INCLUDEを使うことができます。
CREATE INDEX INDEX2 ON TBL1 (COL2) INCLUDE (COL3);
上記のようにすることで、COL3のデータがセカンダリインデックスに含まれるようになります。
EXPLAINで確認しても、セカンダリインデックスが使われていることが分かります。
0: jdbc:phoenix:localhost> EXPLAIN SELECT COL1, COL2, COL3 FROM TBL1 WHERE COL2='222'; +------------+ | PLAN | +------------+ | CLIENT PARALLEL 1-WAY RANGE SCAN OVER INDEX2 ['222'] | +------------+
HBase上では以下のようになっています。Columnとして、COL3の値が格納されています。
$ scan 'INDEX2' ROW COLUMN+CELL 111\x00aaa column=_0:_0, timestamp=1392490841021, value= 111\x00aaa column=_0:_0:COL3, timestamp=1392490841021, value=\x80\x00\x00\x01 222\x00bbb column=_0:_0, timestamp=1392490851138, value= 222\x00bbb column=_0:_0:COL3, timestamp=1392490851138, value=\x80\x00\x00\x02 333\x00ccc column=_0:_0, timestamp=1392490851178, value= 333\x00ccc column=_0:_0:COL3, timestamp=1392490851178, value=\x80\x00\x00\x03 3 row(s) in 0.0550 seconds
これら以外にも、Phoenixは、複合インデックスや降順のインデックスにも対応しています。
以下は、その例になります。COL2とCOL3の複合インデックスであり、COL2に対しては降順のインデックスとなっています。
CREATE INDEX INDEX2 ON TBL1 (COL2 DESC, COL3);
