Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@bigt23

MapR-DB JSONテーブルのSecondary Indexを使ってみる

More than 1 year has passed since last update.

MapR-DB JSONテーブルのSecondary Indexについて、@mitagaki さんが書いてくれました。本記事ではSecondary Indexの使い方について簡単に触れていきます。
今回は既にクラスタが構築されている状況を前提とし、作業としては以下のようになります。順番に見ていきましょう。

  1. ライセンスのチェック
  2. gatewayの準備
  3. テーブル作成
  4. index作成
  5. クエリ実行

1. ライセンスのチェック

Secondary Indexはテーブルのレプリケーション機能を利用して対象のカラムをキーとするテーブルを作成します。
MapRではテーブルのレプリケーション機能を利用する上では商用もしくはテスト用のライセンスが必要となりますので、こちらを適用する必要があります。
テスト用のライセンス(30日間有効)はこちらから作成可能となります。
作成後はMCSやCLIからライセンスを適用してください。

2. gatewayの準備

gatewayとはMapR-DBやMapR-ESのレプリケーションを利用するための機能となり、これを利用してSecondary Index用のテーブルを作成します。
gatewayについては別のエントリで詳細を紹介しますので、今回は簡単にセットアップします。
クラスタのいずれかのノード(今回はnode1)にて以下を実施します。

[node1]$ sudo yum install mapr-gateway
[node1]$ sudo /opt/mapr/server/configure.sh -R
[node1]$ maprcli node services -name gateway -action restart -nodes node1

これでgatewayプロセスが稼働しました。
次にテーブルのレプリケーションの設定ですが、Secondary Index用のテーブルは同一のクラスタに対して作成しますので、以下のようにgatewayの設定を行います。
ちなみに'map60'がクラスタ名となります。

[node1]$ maprcli cluster gateway set -dstcluster mapr60 -gateways node1

3. テーブル作成

JSONテーブルを作成してデータを突っ込んでおきます。
データはこちらのページを参考にいたしました。ありがとうございます!!!

$ mapr dbshell
====================================================
*                  MapR-DB Shell                   *
* NOTE: This is a shell for JSON table operations. *
====================================================
Version: 6.0.1-mapr

MapR-DB Shell
maprdb mapr:> create /user/mapr/db/test
Table /user/mapr/db/test created.

$ hadoop fs -cat /tmp/test.json
{"_id":"2000","USERNAME":"信"    ,"total_score":242, "PROP": {"power": 90, "lead":80, "intelligence":72}  }
{"_id":"2001","USERNAME":"嬴政"  ,"total_score":264, "PROP": {"power": 78, "lead":98, "intelligence":88}  }
{"_id":"2002","USERNAME":"漂"    ,"total_score":233, "PROP": {"power": 78, "lead":75, "intelligence":80}  }
{"_id":"2003","USERNAME":"昌文君","total_score":255, "PROP": {"power": 80, "lead":85, "intelligence":90}  }
{"_id":"2004","USERNAME":"河了貂","total_score":233, "PROP": {"power": 60, "lead":85, "intelligence":88}  }
{"_id":"2005","USERNAME":"壁"    ,"total_score":250, "PROP": {"power": 80, "lead":84, "intelligence":86}  }
{"_id":"2006","USERNAME":"羌瘣"  ,"total_score":262, "PROP": {"power": 95, "lead":80, "intelligence":87}  }
{"_id":"2007","USERNAME":"成蟜"  ,"total_score":199, "PROP": {"power": 55, "lead":68, "intelligence":76}  }
{"_id":"2008","USERNAME":"竭氏"  ,"total_score":195, "PROP": {"power": 50, "lead":65, "intelligence":80}  }
{"_id":"2009","USERNAME":"左慈"  ,"total_score":210, "PROP": {"power": 87, "lead":61, "intelligence":62}  }

$ mapr importJSON -src /tmp/test.json -dst /user/mapr/db/test
# 結果省略

# 結果確認
$ mapr dbshell
====================================================
*                  MapR-DB Shell                   *
* NOTE: This is a shell for JSON table operations. *
====================================================
Version: 6.0.1-mapr

MapR-DB Shell
maprdb mapr:> find /user/mapr/db/test
{"_id":"2000","PROP":{"intelligence":72,"lead":80,"power":90},"USERNAME":"信","total_score":242}
{"_id":"2001","PROP":{"intelligence":88,"lead":98,"power":78},"USERNAME":"嬴政","total_score":264}
{"_id":"2002","PROP":{"intelligence":80,"lead":75,"power":78},"USERNAME":"漂","total_score":233}
{"_id":"2003","PROP":{"intelligence":90,"lead":85,"power":80},"USERNAME":"昌文君","total_score":255}
{"_id":"2004","PROP":{"intelligence":88,"lead":85,"power":60},"USERNAME":"河了貂","total_score":233}
{"_id":"2005","PROP":{"intelligence":86,"lead":84,"power":80},"USERNAME":"壁","total_score":250}
{"_id":"2006","PROP":{"intelligence":87,"lead":80,"power":95},"USERNAME":"羌瘣","total_score":262}
{"_id":"2007","PROP":{"intelligence":76,"lead":68,"power":55},"USERNAME":"成蟜","total_score":199}
{"_id":"2008","PROP":{"intelligence":80,"lead":65,"power":50},"USERNAME":"竭氏","total_score":195}
{"_id":"2009","PROP":{"intelligence":62,"lead":61,"power":87},"USERNAME":"左慈","total_score":210}
10 document(s) found.

4.インデックス作成

今回はtotal_scoreカラムに対してインデックスを貼ります。
インデックス作成は以下のようにmaprcliコマンドを利用して行います。

$ maprcli table index add -path <テーブルへのパス> 
                          -index <インデックス名>
                          -indexedfields <インデックスするフィールド名>
                          [ -includedfields <インデックステーブルに含めるフィールド名> ]
                          [ -hashed [ハッシュインデックスを有効にするかどうか true | false> ]
                          [ -numhashpartitions <ハッシュパーティションの数>]

コマンドの詳細についてはこちらを御覧ください。
ハッシュについては別の記事で紹介しますので、本件ではそれ以外のオプションを使っていきます。

今回は'total_score'フィールドに対してインデックスを貼り、'total_score'順にその他の項目を参照することを考えます。
この場合、indexの貼り方は以下のようになります。

$ maprcli table index add -path /user/mapr/db/test \
                          -index total_score_idx \
                          -indexedfields total_score \
                          -includedfields PROP,USERNAME

比較のためincludedfieldsを全く指定しない場合も作成してみます。
この場合はテーブルの主キー('_id')とインデックス用のフィールドのみのテーブルが作成されます。

$ maprcli table index add -path /user/mapr/db/test \
                          -index total_score_idx2 \
                          -indexedfields total_score

作成後はdbshellのindexscanコマンドを使ってインデックステーブルの中身を参照してみます。

下記のように右端にある'total_score'の順番に各項目が表示されています。
また、インデックス作成時のincludedfields次第で参照できる内容が異なることがわかります。

$ mapr dbshell

maprdb mapr:> indexscan --indexname total_score_idx --t /user/mapr/db/test
{"_id":"2008","USERNAME":"竭氏","PROP":{"intelligence":80,"lead":65,"power":50},"total_score":195}
{"_id":"2007","USERNAME":"成蟜","PROP":{"intelligence":76,"lead":68,"power":55},"total_score":199}
{"_id":"2009","USERNAME":"左慈","PROP":{"intelligence":62,"lead":61,"power":87},"total_score":210}
{"_id":"2002","USERNAME":"漂","PROP":{"intelligence":80,"lead":75,"power":78},"total_score":233}
{"_id":"2004","USERNAME":"河了貂","PROP":{"intelligence":88,"lead":85,"power":60},"total_score":233}
{"_id":"2000","USERNAME":"信","PROP":{"intelligence":72,"lead":80,"power":90},"total_score":242}
{"_id":"2005","USERNAME":"壁","PROP":{"intelligence":86,"lead":84,"power":80},"total_score":250}
{"_id":"2003","USERNAME":"昌文君","PROP":{"intelligence":90,"lead":85,"power":80},"total_score":255}
{"_id":"2006","USERNAME":"羌瘣","PROP":{"intelligence":87,"lead":80,"power":95},"total_score":262}
{"_id":"2001","USERNAME":"嬴政","PROP":{"intelligence":88,"lead":98,"power":78},"total_score":264}
10 document(s) found.

maprdb mapr:> indexscan --indexname total_score_idx2 --t /user/mapr/db/test
{"_id":"2008","total_score":195}
{"_id":"2007","total_score":199}
{"_id":"2009","total_score":210}
{"_id":"2002","total_score":233}
{"_id":"2004","total_score":233}
{"_id":"2000","total_score":242}
{"_id":"2005","total_score":250}
{"_id":"2003","total_score":255}
{"_id":"2006","total_score":262}
{"_id":"2001","total_score":264}
10 document(s) found.

5.クエリ実行

今回は性能の比較などは行いませんが、Secondary Indexが利用されているか確認しましょう

$ sqlline -u jdbc:drill:drillbit=node1 -n mapr -p xxxxxxxx
apache drill 1.13.0-mapr
"json ain't no thang"
0: jdbc:drill:drillbit=node1> select _id,PROP,USERNAME,total_score from dfs.`/user/mapr/db/test` where total_score > 250;
+-------+-------------------------------------------------+-----------+-------------+
|  _id  |                      PROP                       | USERNAME  | total_score  |
+-------+-------------------------------------------------+-----------+-------------+
| 2003  | {"intelligence":90.0,"lead":85.0,"power":80.0}  | 昌文君       | 255.0       |
| 2006  | {"intelligence":87.0,"lead":80.0,"power":95.0}  | 羌瘣        | 262.0       |
| 2001  | {"intelligence":88.0,"lead":98.0,"power":78.0}  | 嬴政        | 264.0       |
+-------+-------------------------------------------------+-----------+-------------+

これだけだと実際にSecondary Indexを使っているのか不明なのでEXPLAINを見てみましょう。

0: jdbc:drill:drillbit=node1> explain plan for select _id,PROP,USERNAME,total_score from dfs.`/user/mapr/db/test` where total_score > 250;
+------+------+
| text | json |
+------+------+
| 00-00    Screen
00-01      Project(_id=[$1], PROP=[$2], USERNAME=[$3], total_score=[$0])
00-02        Scan(table=[[dfs, /user/mapr/db/test]], groupscan=[JsonTableGroupScan [ScanSpec=JsonScanSpec [tableName=maprfs:///user/mapr/db/test, condition=(total_score >
 {"$numberLong":250}), indexName=total_score_idx], columns=[`total_score`, `_id`, `PROP`, `USERNAME`], maxwidth=1]])
以下省略

上述のように、Scanの中でtotal_score_idxテーブルを参照していることがわかります。

それではtotal_score_idxを削除して同様のクエリを実行するとどうなるでしょうか?


$ maprcli table index remove -path /user/mapr/db/test -index total_score_idx

0: jdbc:drill:drillbit=node1> explain plan for select _id,PROP,USERNAME,total_score from dfs.`/user/mapr/db/test` where total_score > 250;
+------+------+
| text | json |
+------+------+
| 00-00    Screen
00-01      Project(_id=[$1], PROP=[$2], USERNAME=[$3], total_score=[$0])
00-02        Scan(table=[[dfs, /user/mapr/db/test]], groupscan=[JsonTableGroupScan [ScanSpec=JsonScanSpec [tableName=maprfs:///user/mapr/db/test, condition=(total_score > {"$numberLong":250})], columns=[`total_score`, `_id`, `PROP`, `US
ERNAME`], maxwidth=1]])
 | {
以下省略


0: jdbc:drill:drillbit=node1> explain plan for select _id,total_score from dfs.`/user/mapr/db/test` where total_score > 250;
+------+------+
| text | json |
+------+------+
| 00-00    Screen
00-01      Project(_id=[$1], total_score=[$0])
00-02        Scan(table=[[dfs, /user/mapr/db/test]], groupscan=[JsonTableGroupScan [ScanSpec=JsonScanSpec [tableName=maprfs:///user/mapr/db/test, condition=(total_score > {"$numberLong":250}), indexName=total_score_idx2], columns=[`total_score`, `_i
d`], maxwidth=1]])
以下省略

上記のように、'PROP', 'USERNAME'フィールドをインクルードするtotal_score_idxを削除した結果、PROP, USERNAMEを参照するクエリではIndexへの参照がなくなりました。
一方で、これらのフィールドを参照しない場合は、total_score_idx2を参照しています。

このようにクエリとインデックステーブルの構成が、Secondary Indexが実際に利用されるかどうかに影響しますので、Secondary Indexを利用する場合はこれらを考慮する必要があります。

ということで今回はSecondary Indexを使ってみる最初の一歩についてご紹介しました。

Secondary Indexを利用するにあたってはSSDが推奨環境となりますので注意しましょう。
またgatewayも今回は1台で間に合わせましたが、本番環境では2つ以上実行してロードバランスを冗長化をもたせることが推奨されます。

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
bigt23

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?