Edited at

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

More than 1 year has passed since last update.


背景

 サーバーに対して位置情報を含む500万レコード以上のデータがFTP経由で送られてくる。送られてくるときは1つのファイルにある独自の方法で圧縮されているが、このままでは位置情報を元にした検査ができないため、受け取った時点で展開して位置情報をサポートしたデータベースに書き込むのが今回の課題。

 なお、このファイルは5分毎に送られてくる。したがって書き込みに5分以上かかってはダメ。できたら アプリケーションでの展開データベースへの書き込み までを2分程度で終わらせて、その後は位置情報をもとに検索ができなければならない。という状況。

 

 親切な方から、位置情報ならElasticsearchがいいんじゃないかと教えてくれた。で、試行錯誤してなんとか2分ちょっとで書き込めるようになったので、そこまでにやったことの覚え書き。


【2017/1/3 追記】参考資料

このメモをキータにあげた際に、Jun Ohtaniさんより、以下の記事を案内いただきました。内容的に素晴らしく、Elasticsearchを使う方は読んでおいて損はないと思いました。

てか、このキータには書いてないことが沢山書いてあるので、こんなキータ読んでるよりこっち読んだ方が

Indexing Performance Tips

Performance Considerations for Elasticsearch Indexing

Performance Considerations for Elasticsearch 2.0 Indexing


JDK

Elasticsearchが稼働するJDKはOpenJDKでもいいが、Elasticsearch 2.4の公式ドキュメントでは Oracle JDK version 1.8.0_73 を推奨している。

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

Ubuntuでのインストール

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

ansible管理の場合は以下のように(参照サイト)。

#--------------------------------------------------------------

# 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_sizethreadpool.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_replicas0 に設定してレプリカが作られないようにする。さらに refresh_interval-1 に設定して書き込みが終わるまでインデキシングが行われないようにする(すべての書き込みが終わったらアプリケーション側からインデキシングを指示する)。

$ curl -XPUT http://localhost:9200/index_name/_settings -d '

{ "index" : { "number_of_replicas" : 0, "refresh_interval" : -1 } }'


ディスク

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


【反則技】Ramdiskを使う

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

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

/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で書き込むようにした。