メモ：Elasticsearchの書き込み性能を上げるためにやったこと

 
## 背景

　サーバーに対して位置情報を含む500万レコード以上のデータがFTP経由で送られてくる。送られてくるときは１つのファイルにある独自の方法で圧縮されているが、このままでは位置情報を元にした検査ができないため、受け取った時点で展開して位置情報をサポートしたデータベースに書き込むのが今回の課題。
　なお、このファイルは５分毎に送られてくる。したがって書き込みに5分以上かかってはダメ。できたら `アプリケーションでの展開` → `データベースへの書き込み` までを2分程度で終わらせて、その後は位置情報をもとに検索ができなければならない。という状況。
　
　親切な方から、位置情報ならElasticsearchがいいんじゃないかと教えてくれた。で、試行錯誤してなんとか2分ちょっとで書き込めるようになったので、そこまでにやったことの覚え書き。
  
  
## 【2017/1/3 追記】参考資料

このメモをキータにあげた際に、[Jun Ohtani](https://twitter.com/johtani)さんより、以下の記事を案内いただきました。内容的に素晴らしく、Elasticsearchを使う方は読んでおいて損はないと思いました。
てか、このキータには書いてないことが沢山書いてあるので、こんなキータ読んでるよりこっち読んだ方が

[Indexing Performance Tips](https://www.elastic.co/guide/en/elasticsearch/guide/current/indexing-performance.html)
[Performance Considerations for Elasticsearch Indexing](https://www.elastic.co/blog/performance-considerations-elasticsearch-indexing)
[Performance Considerations for Elasticsearch 2.0 Indexing](https://www.elastic.co/blog/performance-indexing-2-0)


## JDK

Elasticsearchが稼働するJDKはOpenJDKでもいいが、[Elasticsearch 2.4の公式ドキュメント](https://www.elastic.co/guide/en/elasticsearch/reference/2.4/_installation.html)では `Oracle JDK version 1.8.0_73` を推奨している。

<b>ただ、両方やってみたけど書き込みにかかる時間は同じだった。以下は忘備録</b>

Ubuntuでのインストール

```
$ sudo apt-get install oracle-java8-installer ca-certificates oracle-java8-set-default
```

ansible管理の場合は以下のように（[参照サイト](http://qiita.com/poad1010/items/237e32e081794f0d8823)）。

```
#--------------------------------------------------------------
# aptにリポジトリを追加
#--------------------------------------------------------------
- apt_repository: repo='ppa:webupd8team/java'

#--------------------------------------------------------------
# ライセンスのAcceptをパスするように設定
#--------------------------------------------------------------
- name: Autoaccept license for Java8
  debconf: name='oracle-java8-installer' question='shared/accepted-oracle-license-v1-1' value='true' vtype='select'

#--------------------------------------------------------------
# Oracle JDK8をインストール
#--------------------------------------------------------------
- name: Install Oracle JDK8
  apt: name={{item}} state=latest
  with_items:
    - oracle-java8-installer
    - ca-certificates
    - oracle-java8-set-default

```
 
## 設定ファイル

`threadpool.index.queue_size` と `threadpool.bulk.queue_size` は無制限の `-1` を設定。

```/etc/elasticsearch.yml
threadpool.index.queue_size: -1
threadpool.bulk.queue_size: -1
```
   
## 起動ファイル

`ES_HEAP_SIZE` は実メモリの半分くらい。ただしあまり大きいとJavaのGCが走るのに時間がかかってしまうため、31GBを超えてはいけないとのこと。
今回のインスタンスは128GB積んでるものを使用したため、 `31G` に設定。

```/etc/init.d/elasticsearch
ES_HEAP_SIZE=31g
MAX_OPEN_FILES=165535
MAX_LOCKED_MEMORY=unlimited
```

## パラメータ調整

`number_of_replicas` を `0` に設定してレプリカが作られないようにする。さらに `refresh_interval` を `-1` に設定して書き込みが終わるまでインデキシングが行われないようにする（すべての書き込みが終わったらアプリケーション側からインデキシングを指示する）。

```
$ curl -XPUT http://localhost:9200/index_name/_settings -d '
{ "index" : { "number_of_replicas" : 0,  "refresh_interval" : -1 } }'
```

## ディスク

当然、ディスクが速い方が書き込みも早い。SSDが使えるならSSDがいい。

### 【反則技】Ramdiskを使う
<b>SSDへの書き込みと比べて書き込み速度が特別改善されなかったので、最終的に今回はRamdiskを使用しなかった。以下は忘備録</b>

今回の場合は、データの永続性は必要ない上に溢れるほどメモリがあるため、Ramdiskを使ってみた。なんと15GBをRAMディスクとして使用してみる。

```/etc/fstab
/dev/shm /ramdisk ramfs size=15360m 0 0
```

マウント

```
$ sudo mount -a
```

保存先ディレクトリを作成

```
$ mkdir /ramdisk/elasticsearch
```

起動ファイルを修正

```/etc/init.d/elasticsearch
DATA_DIR=/ramdisk/elasticsearch/$NAME
```

再起動

```
$ /etc/init.d/elasticsearch restart
```

## クライアント

今回は諸事情でアプリケーションにErlang/OTPを採用。 `Erlang/OTP` + `worker_pool` で200プロセスからなるプロセスプールを作り、すべてのプロセスから並列にElasticsearchにbulk_indexで書き込むようにした。