Amazon Elasticsearch Service における Indexing のパフォーマンスチューニング

# 概要

Amazon Elasticsearch Service(以下 Amazon ES)で Indexing のパフォーマンスチューニングを行い、
約3倍の改善が見られたのでそのときやったことで効果が高かったこと、逆に低かったことなどを書きます。

# 前提

チューニング対象の index はウェブサービスなどにおける検索用途のもので、
日時で index を作り直し alias を切り替える運用であるため、
primary shard の調整や Mappings の変更が比較的容易にできる前提です。

# 計測方法

CloudWatch Metrics の `IndexingLatency` や `IndexingRate` で計測します。 

- IndexingLatency : インデックス作成オペレーションを完了するためにシャードで必要な平均時間(ミリ秒)
- IndexingRate : 1分あたりのインデックス作成オペレーションの数

https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-managedomains-cloudwatchmetrics.html

# 効果があったチューニング

基本的に Elastic 社や AWS 社のドキュメントに書かれていることを行っています。
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
https://aws.amazon.com/jp/premiumsupport/knowledge-center/elasticsearch-indexing-performance/

## 使用していない Field の削除　

使用していない Field を Mappings から削除します。
特にコストが高い text を削除すると効果が高いはずです。
このチューニングにより `IndexingLatency` が2倍改善しました。

## インスタンスタイプの変更

専用マスターノードもデータノードも t3 インスタンスをしていましたが、
これは本番用のインスタンスではないため、専用マスターノードを c6g 、データノードを r6g に変更しました。

専用マスターノードはコンピューティング最適化の c6g などを選び、
データノードは汎用もしくはメモリ最適化の m6g, r6g などを選ぶと良いです。
https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/sizing-domains.html#aes-bp-instances

## Shard 数の最適化

実際に入っているデータの容量からサイジングを再度行い、ノード数と Primary Shards を減らしました。
https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/sizing-domains.html

公式ではシャード数に関して多すぎるよりは少なすぎる方が良いとあります。

## bulk size の調整

Elastic 社のドキュメントにあるように徐々に Bulk API の bulk size を上げていき限界値を探ります。
筆者の場合はだいたい 20MB 程度でパフォーマンスが頭打ちになりました。

上記の4つのチューニングが特に効果が高く、ほかは若干改善したかな程度でした。

## refresh_interval の延長

refresh_interval を 60s に引き上げました。若干 `IndexingRate`　が改善しました。
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html#_unset_or_increase_the_refresh_interval

## Auto-Tune

https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/auto-tune.html
導入中のステータスで検証は十分ではありませんが、
Amazon ES のみの機能として Auto-Tune があり、thread pool を自動で最適化することで、
indexing 時の429エラーを回避しやすくなりそうです。

# 効果がなかったチューニング

## ローカルストレージの使用

Elastic 社のドキュメントでは EBS などの Virtualized storage は遅いと指摘があるため、
r6g を NVMe SSD の r6gd に変更してみました。
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html#_use_faster_hardware
が、こちらはほぼパフォーマンスに影響はなかったため r6g にもどしました。

## レスポンスサイズの削減

Elasticsearch への書き込みは [embulk-output-elasticsearch](https://github.com/embulk/embulk-output-elasticsearch) を用いていますが、
以下の箇所に修正を入れ bulk のレスポンスで items のフィールドを除外しました。
https://github.com/embulk/embulk-output-elasticsearch/blob/99e1ee36ea2adf0c0e925f9d4f0ad953a1e44b01/src/main/java/org/embulk/output/elasticsearch/ElasticsearchHttpClient.java#L86
が、indexing のパフォーマンスはほぼ変わりませんでした。
もちろん帯域幅の使用率などには効果があるため、転送速度は向上していると考えられます。

# やらなかったチューニング

## number_of_replicas を0に設定

number_of_replicas を0に設定しましたが、特に改善していなさそうだったのと、
indexing 中は number_of_replicas を0にし、indexing 完了後に戻す必要がありますが、
workflow が複雑になるため採用しませんでした。
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html#_disable_replicas_for_initial_loads

## auto-generated ids の使用

Bulk 時に _id フィールドを使用しないことで auto-generated ids で id が振られ、
シャードを検索するコストが削減されますが、古い Elasticsearch では効果があるが今は十分に高速化されているのと、
Update がしづらくなることから採用しませんでした。
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html#_use_auto_generated_ids

## _source, _all の disable

よく indexing に効くチューニングとして紹介されていますが、
Elastic 社のドキュメントでは enable によるメリットの方が大きいと書かれています。
安易に disable にしない方が良いでしょう。
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html