Apache Drillの性能を測ってみた

# はじめに
Apache DrillはスキーマフリーのSQLクエリエンジンです。
Apache Drillがどのぐらいの性能が出るのか軽く測定してみたので、測定内容と活用シーンに関する所感をご紹介します。

# Apache Drillとは
端的に言えば、HDFS上のファイル（CSV・JSON）やNoSQL（HBase・MongoDB）等に対してSQLを発行できるものです。
特徴としては、非構造化データに対して直接クエリを発行できること、クエリはANSI SQL準拠であることが挙げられます。
詳しくは[公式ドキュメント](https://drill.apache.org/docs/)を参照してみてください。

# やってみたこと
* DrillでCSVファイルに対してSQLを実行したときの性能を測定する。
* 比較対象として同じデータをMariaDBにも投入し、同じSQLを実行したときの性能も測定する。

## 環境
ローカルPC内にVirtualBoxを導入し、仮想マシンを2台立てて測定しました。
ローカルPCのスペックは以下の通りです。

* CPU：Core i7-6500U CPU @ 2.50GHz 2.59GHz
* MEM：16GB

各仮想マシンのリソース割り当ては以下の通りです。

* CPU：2CPU / アクセラレーション有効化
* MEM：6144MB

Drillの導入手順については詳しくご紹介されているサイトがいくつかありました[^1]ので、ここでは概要のみの記載とし、詳細は割愛させていただきます。

[^1]: 以下の記事を大変参考にさせていただきました。
      Apache Drill を使ってみよう！！
      http://azure.sios.jp/archives/jtp-apachedrill-20170308

### 仮想サーバ(1)：Drill
MapR社がDrillインストール済みの仮想アプライアンスを提供していますので、こちらを利用します。
https://mapr.com/products/mapr-sandbox-hadoop/download-sandbox-drill/

ファイル名：MapR-Sandbox-For-Apache-Drill-1.10.0-5.2.2.ova
をダウンロードして、VirtualBoxへ「仮想アプライアンスのインポート」を行います。

### 仮想サーバ(2)：MariaDB
VirtualBoxより仮想マシンを新規作成し、CentOS7の媒体をマウントしてインストールします。
OS起動後はyumでMariaDBをインストールします。
バージョンは5.5.56を利用します。

## テストデータ
### CSVファイル
CSVファイルを2つ用意しました。

* CSVファイルその1：「neko.csv」
    * id：1以上のユニークな数値
    * name（名前）：ランダムな文字列
    * age（年齢）：1～100までのランダムな数値
    * personality_code（性格コード）：1～10のランダムな数値
    
    データのイメージは以下の通りです。

|    id    |    name          |    age    |    personality_code    |
|----------|------------------|-----------|------------------------|
|    1     |    e7b261369b    |    28     |    3                   |
|    2     |    6f12edf39a    |    21     |    5                   |
|    3     |    83f5eb38dc    |    42     |    8                   |
|    4     |    792bba127b    |    34     | 9                      |
|    5     |    46f48bd851    |    80     | 2                      |
|    6     |    75e3dbb099    |    15     |    4                   |
|    7     |    715e2cbfb0    |    44     |    1                   |
|    8     |    748522d354    |    86     |    3                   |
|    9     |    b745f04675    |    9      | 10                     |
|    10    |    50c5abb95e    |    32     |    3                   |
|：|：|：|：|

　　上記のようなデータを**20,480,000**レコード用意しました。

* CSVファイルその2：「personality.csv」　※JOIN用のデータです
    * code（性格コード）：1～10
    * personality（性格）：コード値の説明

　　データのイメージは以下の通りです。

|code|personality|
|----------|-----------------|
|    1     |    friendly     |
|    2     |    curious      |
|    3     |    gracious     |
|    4     |    easygoing    |
|    5     |    brave        |
|    6     |    ambitious    |
|    7     |    funny        |
|    8     |    odd          |
|    9     |    shy          |
|    10    |    violent      |


　　こちらは上記の10レコードのみです。

CSVファイルを作成したら、仮想サーバ(1) のHDFSにPUTしておきます。

```shell
> hadoop fs -put neko.csv personality.csv /data

> hadoop fs -ls /data
Found 8 items
-rw-r--r--   1 mapr mapr    1034773 2017-08-03 16:46 /data/customers.all.csv
drwxr-xr-x   - mapr mapr          1 2017-08-03 16:46 /data/flat
-rwxr-xr-x   1 mapr mapr  502719458 2017-12-13 04:51 /data/neko.csv
drwxr-xr-x   - mapr mapr          1 2017-08-03 16:46 /data/nested
drwxr-xr-x   - mapr mapr         10 2017-08-03 16:46 /data/orders
-rwxr-xr-x   1 mapr mapr        112 2017-12-13 04:51 /data/personality.csv
drwxr-xr-x   - mapr mapr          1 2017-08-03 16:46 /data/products
drwxr-xr-x   - mapr mapr          0 2017-08-03 16:46 /data/views

```

### MariaDBスキーマ
上記のCSVファイルに倣って、仮想サーバ(2) のMariaDBにテーブルを作成します。
テーブル定義は以下の通りで、特にインデックスは張っていません。

```SQL:neko
+------------------+--------------+------+-----+---------+----------------+
| Field            | Type         | Null | Key | Default | Extra          |
+------------------+--------------+------+-----+---------+----------------+
| id               | int(11)      | NO   | PRI | NULL    | auto_increment |
| name             | varchar(255) | YES  |     | NULL    |                |
| age              | int(11)      | YES  |     | NULL    |                |
| personality_code | int(11)      | YES  |     | NULL    |                |
+------------------+--------------+------+-----+---------+----------------+
```

```SQL:personality
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| code        | int(11)      | NO   | PRI | NULL    |       |
| personality | varchar(255) | YES  |     | NULL    |       |
+-------------+--------------+------+-----+---------+-------+
```

テーブル作成が完了したら、CSVファイルからデータをINSERTしておきます。

## クエリ
今回は、以下の5パターンのSELECTクエリを実行してみました。

1. COUNT
2. LIKE
3. JOIN
4. GROUP BY
5. ORDER BY

具体的なSQL文は以下の通りです。

### COUNT
* Drillの場合<br>
テーブル名に相当する部分は、HDFS上のファイルパスをバッククォートで囲った形式で記述します。

```SQL
SELECT COUNT(*) FROM `/data/neko.csv`;
```

* MariaDBの場合

```SQL
SELECT COUNT(*) FROM neko;
```

### LIKE
* Drillの場合<br>

```SQL
SELECT * FROM `/data/neko.csv` WHERE `/data/neko.csv`.name LIKE '%abcd%';
```

* MariaDBの場合

```SQL
SELECT * FROM neko WHERE name LIKE '%abcd%';
```

### JOIN
* Drillの場合<br>

```SQL
SELECT * 
FROM `/data/neko.csv` 
LEFT JOIN `/data/personality.csv` 
  ON `/data/neko.csv`.personality_code=`/data/personality.csv`.code 
WHERE `/data/neko.csv`.age=35;
```

　毎度ファイルパスを記述するのが面倒な場合は、別名記法もいけます

```SQL
SELECT * 
FROM `/data/neko.csv` AS neko 
LEFT JOIN `/data/personality.csv` AS per 
  ON neko.personality_code=per.code
WHERE neko.age=35;
```

* MariaDBの場合

```SQL
SELECT * 
FROM neko 
LEFT JOIN personality 
  ON neko.personality_code=personality.code 
WHERE age=35;
```

### GROUP BY
* Drillの場合

```SQL
SELECT COUNT(*) 
FROM `/data/neko.csv` 
WHERE `/data/neko.csv`.age=50 
GROUP BY `/data/neko.csv`.personality_code;
```

* MariaDBの場合

```SQL
SELECT COUNT(*) 
FROM neko 
WHERE age=50 
GROUP BY personality_code;
```

### ORDER BY
* Drillの場合<br>データはテキスト型として扱われるため、数値型にキャストしてあげないと正しくソートがされないので要注意です。

```SQL
SELECT * 
FROM `/data/neko.csv` 
WHERE age=20 
ORDER BY CAST(`/data/neko.csv`.personality_code AS INT);
```

* MariaDBの場合

```SQL
SELECT * 
FROM neko 
WHERE age=20 
ORDER BY personality_code;
```

## 性能測定
### 測定条件
* 特に負荷のかかっていない状態で単発のクエリを発行します。
* SQLコンソール（Drillであればsqlline、MariaDBであればmysql）上でクエリを実行し、結果表示されるレスポンスタイムを測定数値として採用します。
* WHERE句の条件を微妙に変えつつ、5回実行した結果の平均値を採用しています。
    * 今回の計測では飛び抜けた値（外れ値）は見られませんでした。

### 測定結果
数値の単位はsecです。

| クエリ | 　Drill 　| MariaDB |
|:--|--:|--:|
| COUNT  | 3.97  | 3.19  |
| LIKE  | 6.77  | 7.83  |
| JOIN  | 14.17  | 5.62  |
| GROUP BY  | 4.59  | 4.74  |
| ORDER BY  | 17.26  | 6.81  |

JOINやORDER BYが苦手なようですが、全般的にMariaDBと比較して劇的に遅いという訳でもなさそうです。
無論、MariaDBはインデックスを張っていないので、さらに高速化できる余地は残しています。
とは言え、HDFSに置いただけのテキストファイルにいきなりSQLを投げて、チューニングしていないRDBとほぼ遜色ない性能が出る、となると魅力的に感じます。

# 所感
あらかじめデータ構造や発行するクエリが決まっているようなシーンであれば、やはりRDBの方に分があります。
Drillの使いどころとしては、日々増大していく非構造化データに対してアドホックにクエリを投げたい（多少は時間がかかってもいい）、というケースで力を発揮できるだろうと思いました。