MapR-DB JSON セカンダリインデックスについて

皆様あけましておめでとうございます。昨年中は多忙のため冬休みの宿題とさせていただいていたアドベントカレンダーですが、MapR V6.0で新たにサポートされたMapR-DB JSONのセカンダリインデックスを何回かにわたりご紹介していこうと思います。

# セカンダリインデックスとは

MapR-DB JSONはkey-value型のNOSQL DB同様にKeyを指定したアクセス(プライマリーキー指定、範囲指定、前方一致)では非常に高速なアクセスが可能でしたが、Key以外のデータを絞り込み条件とする場合にはフルスキャンとなっておりました。
今回のバージョンアップでRDBMSのインデックスのように任意のカラムにインデックスを作成できるようになり、より柔軟に様々な分析要件のクエリに対応できるようになりました。

セカンダリインデックスの実体はインデックスで指定されたカラムデータのみを格納する自動管理されたMapR-DB JSONテーブルです。
ベーステーブルに対するデータ変更はテーブルレプリケーションの仕組みを利用して全てセカンダリインデックスに自動反映され、データ参照時はオプティマイザによりセカンダリインデックスの利用可否を判断し、最も効率の良い方式でデータを取得します。


<img src="https://qiita-image-store.s3.amazonaws.com/0/228535/a3094d6a-1ed6-8f4b-9be5-c204832767cf.png" width="1200px">

SQLライクな書式でJavaなどのプログラミング言語からMapR-DB　JSONにアクセスするためのOJAI(Open Json Application Interface) APIとDrillからのSQLアクセスでセカンダリインデックスを利用可能です。

* OJAI(Open Json Application Interface) APIの例

```java

//Establish connection and get tables
Connection conn = DriverManager.getConnection("ojai:mapr:");
DocumentStore employees =conn.getStore("/mapr/dev/tables/employees");

Query query = conn.newQuery()
    .select("name", "address","phone")
    .where("{\"age\": {\"$between\": [25, 35]}}")
    .orderBy("address", Order.DESC) 
    .limit(10)
    .build();
DocumentStream stream = 
    employees.find(query);
```


* DrilでのSQLアクセスの例

```sql

Select name, address,phone
Where age between 25 and 35
Order By address DESC 
limit 10;
```

# セカンダリインデックスの効果
効果はRDMSのテーブルにインデックスを作生するのと同じく、ディスクI/Oの削減による検索パフォーマンスの劇的な改善です。またセカンダリインデックスは幾つかの種類があり、またデータを昇順/降順を選択して格納することが可能ですので用途に応じた使い分けが可能です。
実際にDrillで試してみた結果をご覧ください。

全体で1億件ほどのレコードからプライマリKey以外のカラムを使って3000件弱のレコードを絞り込み、結果を集計するクエリを使ってセカンダリインデックスの効果を見てみました。
 (1)インデックス作成前

```sql

select count(amount),sum(amount) from dfs.`/tables/meisai_json` 
where cust_code  = '35433' 
and   shohin_code ='2941';
+---------+---------+
| EXPR$0  | EXPR$1  |
+---------+---------+
| 2880    | 218880  |
+---------+---------+
1 row selected (5.569 seconds)

※実行プランはこちら。テーブルはフルスキャンです。
00-00    Screen
00-01      Project(EXPR$0=[$0], EXPR$1=[$1])
00-02        StreamAgg(group=[{}], EXPR$0=[COUNT($0)], EXPR$1=[SUM($0)])
00-03          UnionExchange
01-01            Project(amount=[$2])
01-02              Scan(groupscan=[JsonTableGroupScan [ScanSpec=JsonScanSpec [tableName=maprfs:///tables/meisai_json, condition=((cust_code = "35433") and (shohin_code = "2941"))], columns=[`cust_code`, `shohin_code`, `amount`]]])
```



(2)インデックス作成後

```sql

select count(amount),sum(amount) from dfs.`/tables/meisai_json` 
where cust_code  = '35433' 
and   shohin_code ='2941';
+---------+---------+
| EXPR$0  | EXPR$1  |
+---------+---------+
| 2880    | 218880  |
+---------+---------+
1 row selected (0.131 seconds)　 

※実行プランはこちら。インデックスが使用されています。
00-00    Screen
00-01      Project(EXPR$0=[$0], EXPR$1=[$1])
00-02        StreamAgg(group=[{}], EXPR$0=[COUNT($0)], EXPR$1=[SUM($0)])
00-03          Scan(groupscan=[JsonTableGroupScan [ScanSpec=JsonScanSpec [tableName=maprfs:///tables/meisai_json, condition=((cust_code = "35433") and (shohin_code = "2941")), indexName=/tables/meisai_json_index], columns=[`amount`]]])
```


インデックス作成前は5.569 secondsなので約42倍向上しています。

# セカンダリインデックスの主な制限事項
以下に主な制限事項を上げておきます。

*  セカンダリインデックスが有効なMapR-DBテーブル
    * MapR-DB JSONテーブルのみ
*  更新パフォーマンスを優先するためベーステーブルとセカンダリインデックスのデータ同期タイミングは非同期
    * 参照するタイミングにより、ベーステーブルへの変更がセカンダリインデックスに反映されていない場合があります。
* セカンダリインデックスが有効なクエリ
    * OJAI APIからのクエリとDrillを使用したSQLクエリ
    * Hive経由のクエリは対象外
* インデックス数
    * １テーブル当たり32インデックスまで
* インデックスカラムサイズ
    * １インデックスのインデクスカラム長は32KBまで
* インデックスカラムのデータ型
    * インデックス化できるカラムのデータ型は、INTEGER、CHARACTER、BOOLEAN、STRING、BYTE

# まとめ
今回は新規にサポートされたセカンダリインデックスの簡単なご紹介をいたしましたが、次回は用途に応じて使い分け可能なセカンダリインデックスの種類と作成方法についてご紹介する予定です。