Elasticsearch Reindex APIの使い方・基本を徹底解説
はじめに
Elasticsearchは、大量のデータをリアルタイムに検索・分析するための強力な分散システムです。データのインデックス化、検索、集計といった基本的な機能に加え、インデックス構造の変更やデータの移行など、運用中に必要となるさまざまな操作をサポートするためのAPIを提供しています。その中でも特に重要かつ頻繁に利用されるAPIの一つが「Reindex API」です。
インデックスは、Elasticsearchがデータを効率的に検索するために内部的に保持する構造です。しかし、アプリケーションの進化やビジネス要件の変化に伴い、このインデックスの構造や設定を変更する必要が生じることがよくあります。例えば、新しい検索要件に対応するためにフィールドを追加したり、シャード数を変更してパフォーマンスを調整したり、Elasticsearch自体のバージョンアップに伴ってインデックス形式を更新したりする場合などです。
このような状況で、既存のデータを新しい構造や設定を持つインデックスに移行させるために用いられるのがReindex APIです。Reindexは、単にデータをコピーするだけでなく、その過程でデータの変換やフィルタリングを行うことも可能です。これにより、柔軟かつ効率的にインデックスの再構築やデータ移行を実現できます。
この記事では、ElasticsearchのReindex APIについて、その基本的な概念から詳細な使い方、実践的なテクニック、そして運用上の注意点まで、約5000語をかけて徹底的に解説します。この記事を読むことで、Reindex APIを効果的に活用し、Elasticsearchクラスタの運用をより円滑に進めるための知識を習得できるでしょう。
1. Reindexの基本的な考え方と使用ケース
1.1. Reindexの定義:データコピーと変換
ElasticsearchにおけるReindexは、一言で言えば「既存のインデックス(または複数のインデックス)からドキュメントを読み込み、必要に応じて変換処理を施しながら、新しいインデックスに書き込む」プロセスです。これはデータベースにおける SELECT * INTO new_table FROM old_table
のような操作に似ていますが、分散システムであるElasticsearchの特性に合わせて最適化されており、さらにデータの変換処理を組み込める点が大きな違いです。
Reindex APIは、ソースとなるインデックスからドキュメントを取得し、それをデスティネーション(宛先)となるインデックスにインデクシングする操作を内部的に実行します。この際、ソースインデックスの構造とデスティネーションインデックスの構造が完全に一致している必要はありません。デスティネーションインデックスに新しいマッピングや設定が適用されていても、Reindexは可能な限りデータをコピーしようと試みます。マッピングが変更されている場合、Reindexは新しいマッピングに従ってデータをインデクシングします。例えば、ソースインデックスで string
型だったフィールドがデスティネーションで keyword
型になっている場合、Reindexは値を keyword
としてインデクシングします。互換性のない変更(例: text
から integer
など)がある場合は、競合やエラーが発生する可能性があります。
1.2. Reindexの一般的な使用ケース
Reindex APIは多岐にわたるシナリオで活用されます。代表的な使用ケースを以下に挙げます。
- インデックス名の変更: インデックス名を変更したい場合、直接リネームするAPIはありません。Reindexを使って新しい名前のインデックスにデータをコピーするのが一般的な方法です。
- インデックス設定の変更: シャード数、レプリカ数、アナライザー設定など、インデックス作成後に変更できない設定を変更したい場合にReindexが必要です。新しい設定でインデックスを作成し、そこにデータをReindexします。
- マッピングの変更: フィールドタイプの変更(例:
text
からkeyword
)、フィールドの追加、ネストされたオブジェクト構造の変更など、マッピングの破壊的な変更を行う場合、通常は新しいマッピングを持つインデックスを作成してReindexします。破壊的でない変更(例: フィールドの追加)であれば、ReindexなしでPUT /_index/_mapping
APIを使える場合もありますが、既存データに新しいフィールドの値を設定したい場合などはReindexが必要になります。 - Elasticsearchバージョンのアップグレード: Elasticsearchのメジャーバージョンアップ(例: 7.xから8.x)では、内部的なインデックス形式が変更されることがあります。下位互換性が維持されている場合でも、最新の形式でインデックスを最適化するためにReindexが推奨されることがあります。特に、長期的に運用するインデックスや、新しいバージョンで追加された機能(例: ベクタ検索関連のフィールドタイプ)を利用したい場合はReindexが有効です。
- データ移行: あるクラスタから別のクラスタへデータを移行したい場合、リモートReindex機能を利用できます。また、同じクラスタ内でのノード構成変更やデータ配分の最適化のために、Reindexを行うこともあります。
- データ構造の変換・加工: Reindexは、データをコピーする際にスクリプトを使ってドキュメントの内容を変換できます。これは、フィールドの追加、削除、名前変更、値の計算・加工など、ETL(Extract, Transform, Load)パイプラインの一部として利用できます。
- エイリアスを利用したダウンタイムゼロでのインデックス切り替え: 最も重要な使用ケースの一つです。アプリケーションは直接インデックス名を参照するのではなく、エイリアスを参照するように設計しておき、新しいインデックスにデータをReindexした後、エイリアスを新しいインデックスに切り替えることで、アプリケーション側のダウンタイムなしにインデックスを更新できます。この戦略は非常に強力で、Elasticsearchの運用において頻繁に用いられます。
これらの使用ケースを通じて、Reindex APIがElasticsearchの柔軟な運用とメンテナンスにおいて不可欠なツールであることが理解できます。
2. Reindex APIの基本構文とパラメータ
Reindex APIは、POST /_reindex
エンドポイントに対してJSON形式のリクエストボディを送信することで実行されます。リクエストボディには、再インデックス元の情報(source
)と再インデックス先の情報(dest
)を少なくとも含める必要があります。
基本的な構文は以下の通りです。
json
POST _reindex
{
"source": {
"index": "your_source_index(es)",
... (optional source parameters)
},
"dest": {
"index": "your_destination_index",
... (optional dest parameters)
},
... (optional top-level parameters)
}
それぞれのパラメータについて詳しく見ていきましょう。
2.1. 必須パラメータ: source
と dest
source
オブジェクト: 再インデックス元の情報を指定します。
-
index
(必須): 再インデックス元のインデックス名を指定します。- 単一のインデックス名 (例:
"my_logs"
) - 複数のインデックス名をカンマ区切りで指定 (例:
"logs_2023_01,logs_2023_02"
) - ワイルドカードパターンを使用 (例:
"logs_2023_*"
) - インデックス名の配列 (例:
["logs_2023_01", "logs_2023_02"]
) - エイリアス名を指定することも可能です。
- 単一のインデックス名 (例:
-
query
(Optional): 再インデックスするドキュメントをフィルタリングするためのクエリを指定します。Query DSL形式で指定します。このパラメータを使用しない場合、ソースインデックスのすべてのドキュメントがReindexされます。
json
"source": {
"index": "old_index",
"query": {
"term": {
"status": "active"
}
}
}
この例では、status
フィールドが"active"
のドキュメントのみがReindexされます。 -
_source
(Optional): 再インデックスするドキュメントの特定のフィールドのみをコピーするか、または特定のフィールドを除外するかを指定します。- コピーしたいフィールド名の配列 (例:
["title", "body"]
) - コピーしたくないフィールド名の配列 (例:
{"excludes": ["_id", "_version"]}
) true
またはfalse
(例:false
とすると、ソースドキュメント自体はコピーされず、スクリプトで新しいドキュメントを構築する場合などに利用できます)
json
"source": {
"index": "old_index",
"_source": ["title", "content"] // title と content フィールドのみをコピー
}
- コピーしたいフィールド名の配列 (例:
-
size
(Optional): ソースインデックスから取得するドキュメント数の上限を指定します。テストや小さなサブセットの移行に役立ちますが、完全なReindexには通常使用しません。
json
"source": {
"index": "old_index",
"size": 1000 // 最大1000ドキュメントのみReindex
} -
remote
(Optional): リモートのElasticsearchクラスタからデータをReindexする場合に指定します。
json
"source": {
"remote": {
"host": "http://remote_host:9200",
"username": "user",
"password": "password"
},
"index": "remote_index"
}
詳細については後述します。
dest
オブジェクト: 再インデックス先の情報を指定します。
-
index
(必須): 再インデックス先のインデックス名を指定します。このインデックスはReindex実行前に存在している必要があります。存在しない場合は、事前に作成しておく必要があります(設定やマッピングを定義して)。
json
"dest": {
"index": "new_index"
} -
version_type
(Optional): バージョン管理の仕組みを指定します。internal
(デフォルト): Elasticsearch内部でバージョン番号を管理します。ソースドキュメントのバージョンは無視されます。external
: ソースドキュメントのバージョン番号を使用し、デスティネーションインデックスにコピーします。デスティネーションのバージョン番号がソースよりも小さい場合にのみ書き込みを行います。external_gte
: ソースドキュメントのバージョン番号を使用します。デスティネーションのバージョン番号がソース以下のバージョンであれば書き込みを行います。external_gt
: ソースドキュメントのバージョン番号を使用します。デスティネーションのバージョン番号がソース未満のバージョンであれば書き込みを行います。
これらのバージョンタイプは、特に複数のソースから同じデスティネーションにReindexする場合や、データ移行中に更新が発生する可能性がある場合などに、競合を適切に処理するために重要です。
-
op_type
(Optional): ドキュメントのインデクシング操作の種類を指定します。index
(デフォルト): ドキュメントIDが既に存在する場合は上書き、存在しない場合は新規作成します。create
: ドキュメントIDが既に存在する場合はエラーとして扱います(version_conflict_engine_exception
)。新しいドキュメントのみを作成したい場合に使用します。
2.2. その他の主要パラメータ (トップレベル)
source
および dest
オブジェクトと同じレベルで指定するパラメータです。
-
script
(Optional): 再インデックス中に各ドキュメントに対して実行するスクリプトを指定します。データの変換や加工を行う際に非常に強力な機能です。
json
"script": {
"lang": "painless",
"source": "ctx._source.new_field = ctx._source.old_field + '_processed'"
}
ctx._source
は現在のドキュメントソースを表します。詳細については後述します。 -
conflicts
(Optional): ドキュメントのインデクシング中にバージョン競合が発生した場合の挙動を指定します。abort
(デフォルト): 最初の競合が発生した時点でReindex処理を停止します。proceed
: 競合をログに記録し、処理を続行します。Reindexタスクの実行結果に含まれるversion_conflicts
カウントで競合数を把握できます。
-
slices
(Optional): Reindex処理を並列実行するスライスの数を指定します。これにより、Reindexのパフォーマンスを向上させることができます。-1
: Elasticsearchに最適なスライス数を自動的に判断させます(通常、ソースインデックスのシャード数と同等)。- 正の整数: 指定した数のスライスで並列実行します。
json
"slices": 5 // 5つの並列スライスで実行
詳細については後述します。
-
max_docs
(Optional): Reindexするドキュメント数の上限を指定します。指定した数のドキュメントをReindexした時点で処理を終了します。テスト目的や、大規模なインデックスの一部のみをReindexしたい場合に便利です。
json
"max_docs": 10000 // 最大10000ドキュメントをReindex -
timeout
(Optional): 各バッチ処理の待機タイムアウトを指定します。デフォルトは1m
(1分) です。ネットワークの遅延が大きい環境などで調整が必要になる場合があります。 -
requests_per_second
(Optional): Reindex処理がターゲットインデックスに書き込む1秒あたりのリクエスト数を制限します。これにより、Reindexがクラスタに与える負荷を制御できます。-1
: 無制限 (デフォルト)- 正の小数または整数: 指定した数に制限します。
json
"requests_per_second": 100 // 1秒あたり最大100リクエスト
詳細については後述します。
-
wait_for_completion
(Optional): API呼び出しがReindex処理の完了を待つかどうかを指定します。true
(デフォルト): API呼び出しはReindex処理が完了またはエラーになるまでブロックされます。完了後、最終的な実行結果が返されます。false
: API呼び出しはすぐに、実行中のReindexタスクのタスクIDを返して終了します。処理はバックグラウンドで続行されます。タスクの進捗状況は_tasks
APIで確認できます。
これらのパラメータを組み合わせることで、様々なReindexシナリオに対応できます。
3. Reindex APIの実践的な使い方
ここからは、具体的なシナリオに沿ってReindex APIのコード例と実行結果の見方を解説します。例では curl
コマンドを使用しますが、各種クライアントライブラリやKibanaのDev Toolsでも同様のリクエストを送信できます。
前提として、ソースとなるインデックス old_index
が存在し、ターゲットとなるインデックス new_index
は事前に作成済み(必要に応じてマッピングや設定も定義済み)とします。
3.1. 最も単純なReindex
old_index
の全てのドキュメントを new_index
にコピーする、最も基本的なケースです。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}
このリクエストを送信すると、デフォルトでは wait_for_completion=true
なので、Reindex処理が完了するまで待機し、その後に結果が返されます。
実行結果の例:
json
{
"took": 1234, // 処理にかかった時間(ミリ秒)
"timed_out": false, // タイムアウトしたかどうか
"total": 50000, // ソースインデックスの総ドキュメント数(Reindex対象数)
"updated": 0, // 更新されたドキュメント数 (op_typeがindexの場合)
"created": 50000, // 新規作成されたドキュメント数 (op_typeがcreateまたはindexの場合)
"deleted": 0, // 削除されたドキュメント数 (Reindexでは通常0)
"batches": 50, // 処理されたバッチ数
"version_conflicts": 0, // バージョン競合数
"noops": 0, // スクリプト等によって操作されなかったドキュメント数
"retries": { // 再試行回数
"bulk": 0, // バルクインデックス操作の再試行
"search": 0 // ソースから取得するための検索操作の再試行
},
"throttled_millis": 0, // スロットリングによって待機した合計時間(ミリ秒)
"requests_per_second": -1, // 設定されたスロットリング値
"throttled_until_remilled": 0, // 現在のスロットリング待機時間(ミリ秒)
"failures": [], // 処理に失敗したドキュメントのリスト
"task": "..." // wait_for_completion=false の場合に返されるタスクID
}
created
がソースインデックスのドキュメント総数 (total
) と一致しており、failures
が空であれば、Reindexは成功です。
3.2. 特定のドキュメントをReindex
特定の条件を満たすドキュメントのみをReindexしたい場合は、source
オブジェクトに query
パラメータを指定します。
bash
POST _reindex
{
"source": {
"index": "old_index",
"query": {
"range": {
"timestamp": {
"gte": "2023-01-01T00:00:00Z",
"lt": "2023-02-01T00:00:00Z"
}
}
}
},
"dest": {
"index": "new_index"
}
}
この例では、timestamp
フィールドの値が2023年1月中にあるドキュメントのみが new_index
にコピーされます。実行結果の total
は、指定したクエリにマッチしたドキュメント数になります。
3.3. 特定のフィールドのみをReindex
ソースドキュメントの一部のフィールドのみをコピーしたい場合は、source
オブジェクトに _source
パラメータを指定します。
bash
POST _reindex
{
"source": {
"index": "old_index",
"_source": ["title", "author", "publish_date"]
},
"dest": {
"index": "new_index"
}
}
この例では、old_index
のドキュメントのうち、title
, author
, publish_date
の3つのフィールドのみが new_index
にコピーされます。デスティネーションインデックスにこれらのフィールド以外にマッピングが定義されていても、これらのフィールドの値のみが書き込まれます。
3.4. Reindex中のデータ変換 (Scripting)
Reindexの最も強力な機能の一つは、スクリプトを使ってドキュメントの構造や内容を変換できることです。これは、script
パラメータを使用して行います。スクリプトは通常、Painless言語で記述します。スクリプト内では、ctx
というコンテキスト変数を通して現在のドキュメントにアクセスできます。ctx._source
がドキュメントのソースフィールドを格納するマップ(またはハッシュ)です。
例1: フィールド名の変更
old_field_name
というフィールドを new_field_name
に名前変更したい場合。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"script": {
"lang": "painless",
"source": """
if (ctx._source.containsKey('old_field_name')) {
ctx._source.new_field_name = ctx._source.remove('old_field_name');
}
"""
}
}
このスクリプトでは、ctx._source.containsKey('old_field_name')
でフィールドの存在を確認し、存在すれば ctx._source.remove('old_field_name')
で値を抽出しつつフィールドを削除し、その値を ctx._source.new_field_name
に代入しています。
例2: 新しいフィールドの追加と値の設定
既存のフィールドの値に基づいて新しいフィールドを追加したい場合。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"script": {
"lang": "painless",
"source": "ctx._source.full_name = ctx._source.first_name + ' ' + ctx._source.last_name"
}
}
このスクリプトでは、first_name
と last_name
フィールドの値を結合して、新しい full_name
フィールドを作成しています。new_index
のマッピングで full_name
フィールドが定義されている必要があります。
例3: フィールド値の変換
フィールドの値を計算したり、形式を変更したりする場合。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"script": {
"lang": "painless",
"source": """
if (ctx._source.containsKey('price')) {
ctx._source.price_with_tax = ctx._source.price * 1.10; // 消費税10%を加算
}
"""
}
}
このスクリプトでは、price
フィールドに10%の税を加えて price_with_tax
フィールドに格納しています。
スクリプト実行時の注意点:
- パフォーマンス: スクリプトは各ドキュメントに対して実行されるため、複雑なスクリプトや計算コストの高いスクリプトはReindexのパフォーマンスに大きな影響を与えます。できる限りシンプルで効率的なスクリプトを記述することが重要です。
- セキュリティ: Reindexスクリプトは
painless
言語で実行されますが、不正なスクリプトが実行されるリスクを減らすため、スクリプトの利用は信頼できるユーザーに限定すべきです。 - エラー: スクリプト内でエラーが発生すると、そのドキュメントの処理は失敗し、
failures
リストに記録されます。
3.5. 並列処理による高速化 (Slicing)
大規模なインデックスをReindexする場合、単一プロセスで実行すると時間がかかります。slices
パラメータを使用することで、Reindex処理を複数の並列タスクに分割し、処理速度を大幅に向上させることができます。
Reindex APIは、指定されたスライス数に応じてソースインデックスのシャードを分割し、各スライスが独立した検索およびインデクシングタスクを実行します。これにより、複数のノードで同時に処理を進めることが可能になります。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"slices": 8,
"wait_for_completion": false
}
この例では、Reindex処理を8つのスライスに分割しています。wait_for_completion=false
とすることで、API呼び出しはすぐにタスクIDを返してバックグラウンドで実行されます。
スライス数の決定方法:
- ソースインデックスのシャード数: 理想的なスライス数は、通常、ソースインデックスのアクティブなプライマリシャード数と同程度か、それより少し多い数です。例えば、ソースインデックスに5つのプライマリシャードがある場合、
slices: 5
またはslices: 10
(各シャードに対して検索スレッドを複数割り当てる) のように設定することが考えられます。 - クラスタのリソース: スライス数を増やしすぎると、クラスタのCPU、メモリ、ディスクI/O、ネットワーク帯域といったリソースを過剰に消費する可能性があります。クラスタの負荷状況を見ながら適切な数を決定する必要があります。
-1
(自動):slices: -1
と設定すると、Elasticsearchがソースインデックスのシャード数などを考慮して最適なスライス数を自動的に決定します。多くの場合、この設定で十分に高速なReindexが可能です。迷う場合は-1
から試してみると良いでしょう。
スライス数を増やすほど並列度は上がりますが、それに伴ってクラスタへの負荷も増加します。バランスを見ながら設定してください。
3.6. スロットリングによる負荷制御 (requests_per_second
)
Reindex処理は、ソースからの検索とデスティネーションへの書き込みを大量に実行するため、クラスタに大きな負荷をかける可能性があります。特に、本番環境でReindexを実行する際は、ユーザー向けのサービスパフォーマンスに影響を与えないように負荷を制御することが重要です。
requests_per_second
パラメータを使用すると、Reindex処理がデスティネーションインデックスに書き込む1秒あたりのリクエスト数を制限できます。これにより、Reindexが消費するリソースを抑制し、他の処理への影響を軽減できます。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"requests_per_second": 500
}
この例では、1秒あたり最大500件のドキュメントを書き込むようにReindex処理を制限しています。
-1
(デフォルト): 制限なし。最も速くReindexが完了しますが、クラスタへの負荷は最も高くなります。- 正の小数/整数: 指定した数に制限します。値を小さくするほどReindexは遅くなりますが、クラスタ負荷は低減されます。
クラスタの利用状況やReindex対象のデータ量、許容されるReindex時間などを考慮して、適切な値を設定してください。Reindexタスクの実行結果の throttled_millis
フィールドを見ると、スロットリングによって実際に処理が待機した時間の合計を確認できます。これにより、設定したスロットリングが効果を発揮しているか、あるいは制限が厳しすぎるかなどを判断する材料になります。
3.7. 競合処理 (conflicts
)
Reindex中に同じドキュメントIDに対してバージョン競合が発生する可能性があります。これは、特にソースインデックスとデスティネーションインデックスの両方で同じドキュメントが同時に更新される可能性があるような、継続的なデータ移行シナリオなどで起こり得ます。
デフォルトでは、バージョン競合が発生するとReindex処理は停止します (conflicts: "abort"
)。競合を無視して処理を続行したい場合は、conflicts: "proceed"
を指定します。
bash
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"conflicts": "proceed"
}
conflicts: "proceed"
を指定した場合、Reindex処理は競合をログに記録しながら続行します。処理完了後、実行結果の version_conflicts
フィールドで発生した競合の総数を確認できます。競合したドキュメントはReindexされません。
ほとんどの場合、Reindexは新しいインデックスへの一方向のコピーとして実行されるため、意図しない競合は発生しにくいですが、念のため conflicts
パラメータを理解しておくことは重要です。特に継続的な同期など高度なシナリオでは、このパラメータと version_type
パラメータの組み合わせが重要になります。
3.8. リモートクラスタからのReindex (remote
)
別のElasticsearchクラスタに存在するインデックスから、現在のクラスタにデータをReindexすることも可能です。これはクラスタ間のデータ移行や、異なる環境間でのデータ同期に役立ちます。
リモートReindexを行うには、source
オブジェクト内に remote
オブジェクトを指定します。
bash
POST _reindex
{
"source": {
"remote": {
"host": "http://remote_cluster_host:9200",
"username": "elastic",
"password": "changeme"
},
"index": "remote_index_name",
"query": {
"match_all": {}
}
},
"dest": {
"index": "local_index_name"
},
"slices": 10,
"requests_per_second": 200
}
host
: リモートクラスタのホスト名とポート番号を指定します。username
,password
: リモートクラスタへの認証情報を指定します。Elasticsearch Security features (X-Pack) が有効な場合に必要です。APIキーを使用することも可能です。socket_timeout
,connect_timeout
: リモートクラスタへの接続に関するタイムアウトを設定できます。
セキュリティ上の注意点: リモートReindexは、リモートクラスタへの接続情報を設定ファイル (elasticsearch.yml
) またはキーストアに安全に保存することを強く推奨します。特にパスワードをリクエストボディに直接記述するのは避けるべきです。
リモートReindexはネットワーク帯域幅を消費するため、クラスタ間のネットワーク環境を考慮する必要があります。また、スライシングやスロットリングを適切に設定して、両方のクラスタに過負荷がかからないように注意が必要です。
3.9. 非同期実行とタスク管理
大規模なReindex処理は長時間かかる場合があります。デフォルトの wait_for_completion=true
ではAPI呼び出しが完了までブロックされてしまうため、長時間待つ必要があります。このような場合は wait_for_completion=false
を指定して非同期で実行するのが便利です。
bash
POST _reindex?wait_for_completion=false
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}
このリクエストを送信すると、すぐに以下のようなレスポンスが返されます。
json
{
"task": "node_id:task_id"
}
task
フィールドに含まれる node_id:task_id
が、実行中のReindexタスクを一意に識別するIDです。このタスクIDを使用して、タスクの進捗状況を確認したり、タスクをキャンセルしたりできます。
タスクの進捗確認:
_tasks
APIを使用して、指定したタスクの現在の状態を取得できます。
bash
GET _tasks/node_id:task_id
レスポンスには、タスクの開始時刻、実行時間、現在のステータス(完了したドキュメント数、処理中のバッチ数など)、およびReindexタスクの場合はReindex固有の詳細(total, created, updated, failuresなど)が含まれます。
すべてのReindexタスク(または指定した種類のタスク)の一覧を取得することも可能です。
bash
GET _tasks?detailed=true&actions=*reindex
タスクのキャンセル:
実行中のタスクを途中で中断したい場合は、_cancel
エンドポイントを使用します。
bash
POST _tasks/node_id:task_id/_cancel
タスクはすぐに停止され、その時点で処理が完了していないドキュメントはReindexされません。
非同期実行とタスク管理機能を活用することで、Reindex処理の実行中もクラスタの他の操作を継続でき、大規模な移行作業を効率的に進めることが可能になります。
4. Reindexの実行戦略と注意点
Reindexは強力なツールですが、適切に計画・実行しないと、クラスタのパフォーマンス低下や、最悪の場合はサービス停止につながる可能性があります。特に本番環境でReindexを行う際は、以下の戦略と注意点を考慮することが重要です。
4.1. ダウンタイムを最小限に抑える戦略 – エイリアスを利用したゼロダウンタイムReindexing
インデックスの構造変更や設定変更に伴うReindexで最も一般的な課題は、その間のアプリケーションのダウンタイムです。アプリケーションが古いインデックスを参照している間に新しいインデックスへのReindexを行うと、Reindexが完了するまでの間、新しいデータが検索結果に反映されない、あるいはデータの書き込みができないといった問題が発生します。
この問題を解決し、ダウンタイムをゼロまたは最小限に抑えるための標準的なアプローチが、エイリアスを利用した戦略です。
エイリアスは、一つ以上のインデックスを指す名前です。アプリケーションはインデックス名ではなくエイリアス名を使用してデータの読み書きを行います。これにより、エイリアスが指すインデックスを裏側で切り替えるだけで、アプリケーションに影響を与えずにインデックスの更新やメンテナンスを行うことができます。
エイリアスを使ったゼロダウンタイムReindexingのステップ:
-
現在の状況: アプリケーションは
my_app_alias
というエイリアスを使用して、古いインデックスmy_index_v1
にデータを書き込み・読み込みしています。 -
新しいインデックスの準備: 新しい設定やマッピングで、新しいインデックス
my_index_v2
を作成します。このインデックスはまだエイリアスに関連付けられていません。 -
Reindexの実行:
my_index_v1
からmy_index_v2
へデータをReindexします。この際、my_index_v2
はまだアプリケーションからのアクセスがないため、安心してReindexを実行できます。
bash
POST _reindex
{
"source": {
"index": "my_index_v1"
},
"dest": {
"index": "my_index_v2"
}
}
このステップでは、古いインデックスに新しいデータが書き込まれ続ける可能性があります。Reindexが完了するまでの間に古いインデックスに追加または更新されたデータは、自動的には新しいインデックスに反映されません。これをどう扱うか(Reindex完了後に差分を再度Reindexするか、アプリケーション側で二重書き込みを行うかなど)はシナリオによります。 -
エイリアスの切り替え: Reindexが完了したら、エイリアス
my_app_alias
をmy_index_v1
からmy_index_v2
にアトミックに切り替えます。この操作は、以下の_aliases
APIリクエストを一度に実行することで行われます。
bash
POST _aliases
{
"actions": [
{ "remove": { "alias": "my_app_alias", "index": "my_index_v1" } },
{ "add": { "alias": "my_app_alias", "index": "my_index_v2" } }
]
}
このAPI呼び出しは非常に高速に完了し、切り替えの瞬間を除けばアプリケーションは常にエイリアスの指す有効なインデックスにアクセスできます。 -
古いインデックスの削除: エイリアスが
my_index_v2
を指すようになったら、しばらく様子を見て問題がなければ、古いインデックスmy_index_v1
を削除してディスク容量を解放します。
このエイリアス戦略は、Elasticsearchにおける最も一般的なダウンタイムゼロでのインデックス更新手法です。アプリケーションが常にエイリアスのみを使用するように設計しておくことが、この戦略を成功させる鍵となります。
注意点:
- Reindex中に古いインデックスに書き込まれた新しいデータや更新されたデータは、Reindex後の新しいインデックスには含まれません。もしデータの完全な同期が必要であれば、Reindex完了後に差分を移行する仕組み(例: Logstash, 自作ツール)を用意するか、Reindex中にアプリケーション側で古いインデックスと新しいインデックスの両方に書き込む「二重書き込み」戦略を採用する必要があります。
- リアルタイム性が非常に重要なシステムで、Reindex中の差分発生を許容できない場合は、Snapshot/Restoreやより高度なレプリケーションソリューションを検討する必要があるかもしれません。
4.2. パフォーマンスに関する考慮事項
Reindexは、クラスタのリソースを大量に消費する可能性のある操作です。以下の点を考慮してパフォーマンスへの影響を最小限に抑えましょう。
- リソースの消費: Reindexは、ソースインデックスからのデータ読み込み(検索)、デスティネーションインデックスへのデータ書き込み(インデクシング)を行います。これらは、ディスクI/O、CPU、ネットワーク帯域を大量に消費します。特に、シャードが配置されているノードや、デスティネーションインデックスのプライマリシャードがあるノードに負荷が集中しやすくなります。
- スロットリングの利用: 前述の
requests_per_second
パラメータを適切に設定し、Reindexがクラスタ全体に与える負荷を制御します。本番環境では、無制限 (-1
) で実行する前に、テスト環境で適切な制限値を評価することを強く推奨します。 - スライシングの活用:
slices
パラメータを利用してReindexを並列化し、処理時間を短縮します。ただし、並列度を上げすぎると、クラスタ全体のリソース競合を引き起こす可能性があります。クラスタのキャパシティと相談しながら、最適なスライス数を見つけてください。 - インデックス設定の一時的な調整: Reindexの速度を向上させるために、デスティネーションインデックスのリフレッシュ間隔を一時的に長くする(例:
index.refresh_interval
を300s
や-1
に設定)といったチューニングが有効な場合があります。これにより、インデクシング中のディスクI/Oを減らせます。Reindex完了後に元の設定に戻すのを忘れないでください。また、レプリカシャードの数を一時的に減らすことも検討できます(これにより書き込み時のオーバーヘッドが減りますが、Reindex中の読み込み耐障害性は低下します)。 - クラスタ監視: Reindex実行中は、クラスタのメトリクス(CPU使用率、メモリ使用率、ディスクI/O、ネットワークトラフィック、検索レイテンシ、インデクシングスループットなど)を注意深く監視し、問題が発生していないか確認することが不可欠です。KibanaのMonitoring機能や他の監視ツールを活用しましょう。
4.3. エラーハンドリングとデバッグ
Reindex処理中にエラーが発生することは珍しくありません。エラーに適切に対処する方法を知っておくことが重要です。
- APIレスポンスの確認:
wait_for_completion=true
で実行した場合、レスポンスボディのfailures
リストを確認します。このリストには、処理に失敗した各ドキュメントに関する情報(インデックス名、ドキュメントID、エラータイプ、原因など)が含まれています。 - タスク情報の確認:
wait_for_completion=false
で実行した場合、またはwait_for_completion=true
のレスポンスにfailures
が含まれている場合は、タスクIDを使用して_tasks
APIで詳細なタスク情報を取得します。ここにもfailures
リストや、タスク全体の実行結果に関する情報が含まれています。 - Elasticsearchログ: Elasticsearchのサーバーログ(各ノードのログファイル)には、Reindex処理中に発生したエラーに関するより詳細な情報が出力されている場合があります。ログレベルをデバッグレベルに上げることで、さらに詳しい情報を得ることも可能ですが、これは本番環境では慎重に行う必要があります。
- 競合エラー (
conflicts
): バージョン競合が原因で失敗している場合は、conflicts: "proceed"
を指定して続行させるか、version_type
の設定を見直す必要があります。 - スクリプトエラー: スクリプトの構文エラーや実行時エラーは、
failures
リストやログにscript_exception
として記録されます。スクリプトの内容を見直し、構文やロジックに誤りがないか確認してください。 - リソース不足:
RejectedExecutionException
のようなエラーが多発する場合、クラスタのリソース(キュー容量、スレッドプールなど)が不足している可能性があります。スロットリングを厳しくするか、クラスタをスケールアップすることを検討してください。
4.4. メモリとディスク容量
Reindexは、ソースインデックスとデスティネーションインデックスの両方をディスク上に保持する必要があるため、Reindex実行中は追加のディスク容量が必要です。Reindex対象のデータ量と同程度の空き容量を事前に確保しておくことが望ましいです。
また、Reindex処理はJVMヒープメモリとOSのページキャッシュも消費します。ソースからのデータ取得(検索)とデスティネーションへの書き込み(インデクシング)の両方が行われるため、これらの操作に必要なメモリリソースが適切に確保されているか確認が必要です。特に大きなドキュメントを扱う場合や、スクリプトで複雑な処理を行う場合は、メモリ消費量が増加する可能性があります。
5. より高度なトピック
5.1. ReindexとSnapshot/Restoreの比較
インデックスのバックアップや移行方法として、Reindex以外にSnapshot/Restoreがあります。それぞれの使い分けを理解しておきましょう。
- Reindex:
- 目的: インデックス構造や設定の変更、データの変換・加工、バージョンアップに伴う形式更新、クラスタ内でのデータ移行。
- 利点: データのフィルタリングや変換が可能。エイリアスと組み合わせてダウンタイムゼロ更新が可能。特定のインデックスのみを対象にできる。
- 欠点: クラスタのリソースを消費する。Reindex中に発生した差分データの考慮が必要。クラスタ全体のバックアップには不向き。
- Snapshot/Restore:
- 目的: クラスタまたはインデックスのバックアップと復元、クラスタ間での完全なデータコピー。
- 利点: クラスタの状態(設定、データ、ユーザー情報など)を完全にバックアップ・復元できる。データロスなく特定の時点の状態に戻せる。Reindexよりも効率的な場合がある。
- 欠点: データのフィルタリングや変換はReindexほど柔軟ではない(一部フィルタリング機能はある)。復元時には通常、対象インデックスが利用できなくなる(読み込み専用になるなど、ダウンタイムが発生しやすい)。
インデックスの構造変更やデータ変換が主目的の場合はReindexが適しています。クラスタ全体のバックアップや、ある時点の完全なコピーが必要な場合はSnapshot/Restoreが適しています。
5.2. ReindexとUpdate By Query/Delete By Queryの比較
特定の条件にマッチするドキュメントを一括で更新または削除したい場合、Update By Query APIやDelete By Query APIを使用できます。これらのAPIも内部的にはReindexと同様のメカニズム(ScrollとBulk APIの利用)を使用しています。
- Update By Query: 条件にマッチするドキュメントに対して、スクリプトを使用してその場で更新を行います。新しいインデックスを作成する必要はありません。
- Delete By Query: 条件にマッチするドキュメントを一括で削除します。
これらのAPIは、既存のインデックス内でドキュメントを更新・削除するのに適しています。一方、Reindexは新しいインデックスにデータをコピーするのが目的であり、その過程で構造変更や広範な変換を行う場合に適しています。既存のインデックスの構造を変えずに、単にドキュメントの内容を更新したいだけであれば、Update By Queryの方がシンプルで適しています。
5.3. バージョンアップに伴うReindex
Elasticsearchのメジャーバージョンアップ時には、インデックスの内部フォーマットが更新されることがあります。通常、古いバージョンのインデックスは新しいバージョンでも読み取り可能ですが、書き込み性能の最適化や、新しいバージョンで導入された機能を完全に活用するためには、Reindexによるインデックスフォーマットの更新が推奨されることがあります。
Elasticsearchのアップグレードガイドには、どのバージョン間でReindexが必要か、または推奨されるかについての詳細な情報が記載されています。アップグレード計画時には、必ず公式ドキュメントを参照し、Reindexが必要か、そして必要な場合の手順を確認してください。
6. まとめ
この記事では、ElasticsearchのReindex APIについて、その基本的な機能から詳細なパラメータ、実践的な使い方、そして運用上の重要な考慮事項までを解説しました。
Reindex APIは、Elasticsearchクラスタを運用する上で避けて通れない、インデックス構造の変更、データ移行、およびバージョンアップなどのメンテナンス作業において、非常に強力で柔軟なツールです。スクリプトによるデータ変換、スライシングによる並列処理、スロットリングによる負荷制御といった機能により、様々なユースケースに対応し、大規模なデータセットに対しても効率的に処理を実行できます。
特に、エイリアスを利用したダウンタイムゼロでのインデックス切り替え戦略は、本番環境でのReindexにおいて最も重要なテクニックの一つです。この戦略をマスターすることで、ユーザーへの影響を最小限に抑えながら、継続的にクラスタを改善・更新していくことが可能になります。
Reindexを実行する際は、クラスタのリソース状況を十分に把握し、スロットリングやスライス数を適切に設定して負荷を制御することが不可欠です。また、非同期実行とタスク管理機能を活用することで、Reindex処理の進捗を追跡し、必要に応じて介入できるようになります。
この記事で解説した内容を参考に、ElasticsearchのReindex APIを安全かつ効果的に活用し、皆様のElasticsearch運用がよりスムーズになることを願っています。
7. 参考文献/関連リソース
- Elasticsearch 公式ドキュメント: Reindex API (最新バージョンに合わせて参照してください)
- Elasticsearch 公式ドキュメント: Update By Query API
- Elasticsearch 公式ドキュメント: Delete By Query API
- Elasticsearch 公式ドキュメント: Aliases API
- Elasticsearch 公式ドキュメント: Task Management API
- Elasticsearch 公式ドキュメント: Painless Scripting Language
これらの公式ドキュメントは、Reindex APIの最新の情報、全てのパラメータの詳細、およびより高度な機能について確認するための最も信頼できる情報源です。
注記: この記事は約5000語を目指して記述されていますが、実際の文字数はテーマの深掘り具合や詳細なコード例の量によって変動します。上記は、ユーザーが要求した情報量と内容を網羅するために構成されたものです。