背景
サーバーに対して位置情報を含む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_size
と threadpool.bulk.queue_size
は無制限の -1
を設定。
threadpool.index.queue_size: -1
threadpool.bulk.queue_size: -1
起動ファイル
ES_HEAP_SIZE
は実メモリの半分くらい。ただしあまり大きいとJavaのGCが走るのに時間がかかってしまうため、31GBを超えてはいけないとのこと。
今回のインスタンスは128GB積んでるものを使用したため、 31G
に設定。
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を使う
SSDへの書き込みと比べて書き込み速度が特別改善されなかったので、最終的に今回はRamdiskを使用しなかった。以下は忘備録
今回の場合は、データの永続性は必要ない上に溢れるほどメモリがあるため、Ramdiskを使ってみた。なんと15GBをRAMディスクとして使用してみる。
/dev/shm /ramdisk ramfs size=15360m 0 0
マウント
$ sudo mount -a
保存先ディレクトリを作成
$ mkdir /ramdisk/elasticsearch
起動ファイルを修正
DATA_DIR=/ramdisk/elasticsearch/$NAME
再起動
$ /etc/init.d/elasticsearch restart
クライアント
今回は諸事情でアプリケーションにErlang/OTPを採用。 Erlang/OTP
+ worker_pool
で200プロセスからなるプロセスプールを作り、すべてのプロセスから並列にElasticsearchにbulk_indexで書き込むようにした。