Elasticsearch reindexとは?基本的な使い方と目的

Elasticsearch reindexとは?基本的な使い方と目的 の詳細な説明

はじめに:Elasticsearch reindexとは?

Elasticsearchは、大量の構造化・非構造化データをリアルタイムで検索、分析、格納するための分散型検索・分析エンジンです。その柔軟性とスケーラビリティから、ログ分析、全文検索、メトリクス監視など、幅広い用途で利用されています。

Elasticsearchでデータを扱う際、時には既存のデータを別のインデックスへコピーしたり、構造を変更したり、特定の条件で絞り込んだりする必要が生じます。このような操作を行うための強力なツールが、Elasticsearchの Reindex API です。

Reindex APIは、一つ以上のソースインデックスからドキュメントを読み込み、それらを指定されたターゲットインデックスに書き込む機能を提供します。単純なコピーだけでなく、データのフィルタリング、変換、複数のインデックスのマージなども同時に行うことができます。

この操作は、特に以下のようなシナリオで不可欠となります。

  • インデックスの設定(シャード数、レプリカ数など)やマッピング(フィールドの型やアナライザー)を変更したい場合。
  • Elasticsearchのメジャーバージョンをアップグレードする場合。
  • 古いデータ形式から新しいデータ形式へ移行する場合。
  • 複数のインデックスに分散しているデータを一つに集約したい場合。
  • 特定の条件を満たすドキュメントだけを新しいインデックスに移したい場合。

本記事では、ElasticsearchのReindex APIについて、その基本的な使い方から、多岐にわたる目的、詳細な設定方法、パフォーマンスに関する考慮事項、そして実践的なベストプラクティスに至るまで、網羅的に解説します。約5000語というボリュームで、Reindexに関するあらゆる疑問に応えることを目指します。

Elasticsearchの基本構造とReindexの立ち位置

Reindexを理解するためには、まずElasticsearchにおけるデータの構造と変更の考え方を知っておく必要があります。

Elasticsearchでは、データは ドキュメント という単位で管理されます。ドキュメントはJSON形式で、フィールドとその値のペアから構成されます。ドキュメントは インデックス と呼ばれる論理的な名前空間に格納されます。インデックスはRDBにおけるデータベースやテーブルに似ていますが、より柔軟なスキーマレスな特性(厳密には動的マッピング)を持っています。

インデックスは、物理的には シャード と呼ばれる複数のセグメントに分割されてクラスター内のノードに分散されます。これにより、大量のデータを水平分散して格納・検索することが可能になります。シャードはさらに、可用性と検索性能向上のために レプリカ と呼ばれる複製を持つことができます。

Elasticsearchにおけるドキュメントの追加、更新、削除は、Luceneのセグメントという構造に書き込むことで行われます。重要なのは、Luceneのセグメントは一度書き込まれると変更できない(immutable、不変)という特性を持っている点です。ドキュメントの更新は、実際には新しいバージョンのドキュメントを新しいセグメントに書き込み、古いバージョンのドキュメントを削除済みとしてマークすることで実現されます。削除も同様に、削除済みマークを付けることで行われます。

このようなアーキテクチャのため、既存のインデックスの設定(シャード数やマッピング)を後から変更することは、通常困難または不可能です。例えば、インデックス作成後にシャード数を変更することは直接的にはできません。マッピングに関しても、既存のフィールドのデータ型を変更したり、フィールド名を変更したりすることは、既存のドキュメントに格納されているデータとの整合性の問題から、基本的には許可されていません。

ここでReindexの出番となります。Reindexは、既存のインデックス(ソースインデックス)からドキュメントを「読み込み」、そのドキュメントを必要に応じて変換し、「新しいインデックス」(ターゲットインデックス)に「書き込み」ます。つまり、新しいインデックスは最初から望ましい設定(シャード数、マッピング、アナライザーなど)で作成しておき、そこに古いインデックスのデータを移し替えるというアプローチを取ります。

これは引っ越しに例えることができます。古い家に住んでいたが、新しい家の間取りや設備(インデックス設定やマッピング)が良いので、古い家から荷物(ドキュメント)を運び出し、新しい家に運び入れる、というイメージです。Reindexは、この「荷物を運び出す」と「新しい家に運び入れる」プロセスを、Elasticsearchクラスター内で行うための機能なのです。

Reindexの目的と主要なユースケース

Reindexは単にデータをコピーするだけでなく、様々な目的のために利用されます。主なユースケースを以下に詳述します。

1. インデックス設定の変更

Elasticsearchのインデックス設定は、作成時に多くのパラメータが決定されます。一度作成されたインデックスでは、いくつかの設定(例えばレプリカ数)は後から変更可能ですが、シャード数マッピング(フィールド定義、データ型、アナライザーなど) のような重要な設定は、後から直接変更することができません。

  • シャード数の変更: アプリケーションの成長に伴い、インデックスのデータ量が予測を超えて増加した場合、当初設定したシャード数ではパフォーマンスが劣化することがあります。また、逆にデータ量が少なく、シャード数が多すぎると管理コストや検索性能に悪影響が出る場合もあります。適切なシャード数に調整するためには、新しいシャード数を持つインデックスを作成し、Reindexでデータを移行する必要があります。
  • マッピングの変更:
    • データ型の変更: あるフィールドのデータ型を、例えば text から keyword に変更したい場合や、long から double に変更したい場合など。既存のデータがその新しい型で適切にインデックス化されるように、Reindexが必要になります。
    • フィールド名の変更: フィールド名を変更したい場合、単純に新しい名前でドキュメントをインデックス化し直す必要があります。Reindex時にスクリプトやIngest Pipelineを使ってフィールド名を変更しながらデータをコピーします。
    • アナライザーの変更/追加: 全文検索の精度を向上させるために、カスタムアナライザーを導入したり、既存のアナライザー設定を変更したりする場合。新しいアナライザーはインデックス作成時に指定する必要があるため、Reindexが必要になります。
    • 新しいフィールドの追加(デフォルト値付き): 既存のドキュメントに新しいフィールドを追加したい場合で、かつ Reindex 時に特定のデフォルト値を設定したり、他のフィールドの値から計算して値を設定したりしたい場合。

2. Elasticsearchバージョンのアップグレード

Elasticsearchは活発に開発されており、メジャーバージョンアップごとに多くの新機能が追加され、内部構造やAPIに変更が加えられることがあります。特にメジャーバージョンアップグレード時には、旧バージョンで作成されたインデックスが新バージョンと完全に互換性を持たない場合があります。

このような場合、アップグレード後のElasticsearchバージョンで新しいインデックスを作成し、旧バージョンのインデックスからReindexすることで、データを新しいバージョンと互換性のある形式に移行します。これは、アップグレードガイドでも推奨される重要なステップです。新しいバージョンで導入された新しいマッピングタイプや機能を活用するためにもReindexが利用されます。

3. データの再編成/クリーンアップ

Reindex APIは、単なるコピーにとどまらず、コピーするドキュメントをフィルタリングしたり、データを変換したりする機能を持っています。

  • 古いドキュメントのフィルタリング: 時系列データなどにおいて、一定期間経過した古いドキュメントを新しいインデックスにはコピーしない、といったフィルタリングが可能です。source にクエリを指定することで実現できます。
  • 特定の条件を満たすドキュメントの移行: 特定のステータスを持つドキュメントだけを別のインデックスに隔離したい、といった場合に利用できます。これも source.query で実現します。
  • データの変換/加工: ドキュメントのコピー時に、フィールドの値の変更、フィールドの追加/削除、複数のフィールドを結合して新しいフィールドを作成、といった様々なデータ加工を行うことができます。これは Reindex API の script パラメータや dest.pipeline パラメータ(Ingest Pipeline)を使って実現します。例えば、ユーザーの名前と姓を結合してフルネームフィールドを作成したり、日付フォーマットを変更したりといったことが可能です。

4. 複数のインデックスの統合

用途に応じて複数のインデックスにデータを分散させていたが、分析や管理の都合上、それらを一つのインデックスにまとめたい場合があります。

  • 時系列データの集約: 例えば、日ごとに作成していたログインデックス (logs-2023-10-26, logs-2023-10-27, …) を、月ごと (logs-2023-10) や年ごと (logs-2023) のインデックスにまとめ直す、といったシナリオです。source.index に複数のインデックス名やワイルドカードを指定することで実現できます。

5. インデックス間のデータコピー

純粋に、既存のインデックスのデータを別のインデックスにコピーしたい場合にもReindexが使われます。

  • テスト環境へのデータコピー: 本番環境のデータを部分的にテスト環境にコピーして、開発やテストを行う。
  • バックアップ目的 (限定的): スナップショット/リストアが主なバックアップ手段ですが、特定のインデックスだけを別の場所やクラスターにコピーしておきたい場合にReindexが利用されることもあります。(ただし、スナップショット/リストアの方が効率的で推奨されます。)

6. インデックスエイリアスを利用したダウンタイムなしの更新

Reindexは新しいインデックスにデータをコピーするため、このプロセスをインデックスエイリアスと組み合わせることで、アプリケーションからの参照をダウンタイムなしで切り替えることが可能です。

  • アプリケーションはインデックス名ではなくエイリアス名を見てデータを読み書きする。
  • 新しい設定(シャード数やマッピングなど)で新しいインデックスを作成する。
  • 古いインデックスから新しいインデックスへReindexを行う。
  • Reindexが完了したら、エイリアスが古いインデックスを指していた状態から、新しいインデックスを指すようにアトミックに切り替える。
  • アプリケーションは意識することなく、新しいインデックスを参照するようになる。
  • 古いインデックスは削除する。

このパターンは、設定変更を伴うインデックスの更新において、最も一般的に推奨されるベストプラクティスです。

Reindex APIの基本的な使い方

Reindex APIは、ElasticsearchのREST APIを通じて操作します。基本的には POST _reindex エンドポイントに対してJSON形式のリクエストボディを送信します。

基本的なリクエストボディは、コピー元の情報を指定する source と、コピー先の情報を指定する dest で構成されます。

json
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}

このリクエストは、old_index の全てのドキュメントを読み込み、新しいインデックス new_index に書き込みます。new_index が存在しない場合は、デフォルト設定で自動的に作成されます。

各パラメータについて詳しく見ていきましょう。

source パラメータ

コピー元のインデックスやドキュメントの指定を行います。

  • index:
    • コピー元のインデックス名を指定します。単一のインデックス名 ("my_index")、複数のインデックス名を配列で指定 (["index1", "index2"])、またはワイルドカード ("logs-*") で指定できます。複数のインデックスを指定した場合、それらの全てのドキュメントが読み込まれてターゲットインデックスに書き込まれます(複数のインデックスを統合するユースケースで利用されます)。
  • query:
    • コピーするドキュメントをフィルタリングするためのクエリを指定します。指定されたクエリにマッチするドキュメントだけがコピーされます。Query DSL形式で記述します。例: {"term": {"status": "active"}}。このパラメータは、特定のドキュメントだけを移行したい場合に便利です。
  • _source:
    • コピーするフィールドを限定することができます。デフォルトでは全てのフィールドがコピーされます。配列でフィールド名のリストを指定します (["field1", "field2"])。除外したいフィールドを指定する場合は exclude を利用できます ({"exclude": ["sensitive_data"]})。これは、ターゲットインデックスに不要なフィールドを含めたくない場合や、ネットワーク帯域幅/ディスクIOを節約したい場合に有効です。
  • size:
    • Reindexプロセスが一度に処理するドキュメントのバッチサイズ(スクロールAPIのページサイズ)を制御します。デフォルトは1000です。大きな値を設定するとスループットが向上する可能性がありますが、メモリ使用量が増加します。通常はデフォルト値で問題ありませんが、OutOfMemoryErrorが発生する場合や、特定のパフォーマンス特性を得たい場合に調整します。
  • remote:
    • リモートクラスター からデータをReindexする場合に指定します。このパラメータは後述の「リモートクラスターからのReindex」セクションで詳しく説明します。

dest パラメータ

コピー先のインデックスや書き込み方法の指定を行います。

  • index:
    • コピー先のインデックス名を指定します。単一のインデックス名のみ指定可能です。指定したインデックスが存在しない場合、Reindex実行時にデフォルト設定で自動的に作成されます。既に存在する場合、ドキュメントは追加または上書きされます(op_type の設定による)。
  • version_type:
    • ドキュメントのバージョン情報の扱いを指定します。これは、主にReindex中にソースインデックスでドキュメントが更新された場合の競合解決や、外部システムからのデータ取り込み時にバージョン情報を引き継ぎたい場合に利用します。
      • internal (デフォルト): Elasticsearchが内部的にバージョン番号を管理します。Reindex実行中にソースドキュメントが更新されても、その更新はターゲットインデックスには反映されません(Reindex開始時点のバージョンのドキュメントがコピーされます)。
      • external: ソースドキュメントのバージョン番号をターゲットドキュメントに引き継ぎます。ソースドキュメントのバージョンがターゲットドキュメントの現在のバージョンよりも新しい場合にのみ書き込みが行われます。
      • external_gte: external と似ていますが、ソースバージョンがターゲットバージョン以上の場合に書き込みを行います。
  • op_type:
    • ターゲットインデックスへの書き込み操作タイプを指定します。
      • index (デフォルト): ドキュメントIDが既に存在する場合、既存のドキュメントを上書きします。
      • create: ドキュメントIDが既に存在する場合、書き込みをスキップまたは失敗させます(conflicts パラメータによる)。これは、ソースインデックスのドキュメントをターゲットインデックスに追加したいが、重複は避けたい場合に利用します。conflicts=proceed と組み合わせることで、重複エラーを無視して処理を続行できます。
  • routing:
    • ターゲットインデックスへの書き込み時に、ドキュメントのルーティングを指定します。
      • デフォルトでは、ソースドキュメントのルーティング値が引き継がれます。
      • "_key": ソースドキュメントのIDをルーティング値として使用します。
      • "=_key": ソースドキュメントのIDをルーティング値として使用しますが、IDがない場合はランダムなルーティングになります。
      • カスタム文字列: 指定した静的な文字列を全てのドキュメントのルーティング値として使用します。
  • pipeline:
    • 後述する Ingest Pipeline を利用して、ドキュメントをターゲットインデックスに書き込む前に変換する場合に、パイプラインIDを指定します。
  • version_conflict:
    • 非推奨です。代わりに conflicts パラメータを使用してください。

その他のパラメータ

Reindexリクエストのトップレベルには、上記 sourcedest 以外にも様々なパラメータを指定できます。

  • conflicts:
    • Reindex中にバージョン競合(op_typecreate でドキュメントが既に存在する場合など)が発生した場合の挙動を指定します。
      • abort (デフォルト): 競合が発生した場合、Reindexタスクを中止します。
      • proceed: 競合が発生したドキュメントをスキップし、警告ログを出力して処理を続行します。多くのケースでこちらを選択することが推奨されます。
  • script:
    • 後述する スクリプト を利用して、ドキュメントの変換を行う場合に指定します。Ingest Pipeline が利用可能な場合は、通常 Ingest Pipeline が推奨されます。
  • slices:
    • Reindexタスクを複数の並行して実行されるサブタスク(スライス)に分割します。これにより、Reindex処理を並列化して高速化できます。シャード数やノード数、クラスターのリソースに応じて適切な値を設定することが重要です。auto を指定すると、Elasticsearchが適切なスライス数を自動的に判断します(通常、ソースインデックスのシャード数と同じか、それより少ない値)。
  • requests_per_second:
    • 後述する スロットリング 設定です。Reindexがクラスターに過負荷をかけないように、1秒あたりのドキュメント書き込みリクエスト数を制限します。-1 (デフォルト) は制限なしを意味します。
  • timeout:
    • Reindexタスクがタイムアウトするまでの時間を指定します(例: 5m for 5 minutes)。デフォルトは無制限です。
  • wait_for_completion:
    • true (デフォルト): Reindexタスクが完了するまでリクエストがブロックされます(同期実行)。
    • false: リクエストは即座にtaskIdを返し、Reindexタスクはバックグラウンドで非同期に実行されます。長時間のReindexタスクを実行する場合に推奨されます。非同期実行については後述します。

基本的な実行例

例えば、products_v1 インデックスのドキュメントを、新しいマッピングを持つ products_v2 インデックスにコピーする場合。

json
POST _reindex
{
"source": {
"index": "products_v1"
},
"dest": {
"index": "products_v2"
}
}

特定のカテゴリの製品だけをコピーする場合。

json
POST _reindex
{
"source": {
"index": "products_v1",
"query": {
"term": {
"category.keyword": "electronics"
}
}
},
"dest": {
"index": "products_v2"
}
}

Reindex APIの詳細設定と高度な使い方

非同期実行 (Task API)

デフォルトでは wait_for_completion=true であり、Reindexタスクが完了するまでクライアントからのリクエストがブロックされます。しかし、大規模なインデックスのReindexは数分、数時間、あるいはそれ以上の時間がかかる場合があります。このような場合、リクエストがタイムアウトしたり、クライアントが切断されたりするリスクがあります。

そこで、wait_for_completion=false パラメータを使用してReindexタスクを非同期で実行することが推奨されます。

json
POST _reindex?wait_for_completion=false
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}

このリクエストを送信すると、Elasticsearchは即座にタスクIDを含むレスポンスを返します。

json
{
"task": "node_id:task_id"
}

この task_id を使用して、タスクの進捗状況を監視したり、キャンセルしたりすることができます。

  • タスクの進捗状況の確認:
    json
    GET _tasks/node_id:task_id

    このAPIは、タスクの現在のステータス(完了したドキュメント数、処理中のドキュメント数、エラーなど)を返します。
  • タスクのキャンセル:
    json
    POST _tasks/node_id:task_id/_cancel

    実行中のReindexタスクを中止します。中止されたタスクは、その時点までに処理されたドキュメントのみがターゲットインデックスに書き込まれます。
  • 完了したタスクの確認:
    デフォルトでは、完了したタスクの情報はしばらくElasticsearchに保持されます。GET _cat/tasks?detailed などで確認できます。不要になったタスク情報はクリーンアップされるか、明示的に削除できます(通常は不要)。

スクリプトによるデータ変換

Reindex時にドキュメントのデータを変換したい場合、script パラメータを使用できます。例えば、既存のフィールドの値を変更したり、新しいフィールドを追加したりといったことが可能です。

json
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"script": {
"lang": "painless",
"source": """
// priceフィールドを10%値上げする
ctx._source.price *= 1.1;
// statusフィールドが存在しない場合は"pending"を追加
if (ctx._source.status == null) {
ctx._source.status = "pending";
}
// 不要なフィールドを削除
ctx._source.remove("temp_field");
"""
}
}

script パラメータ内で、Painlessスクリプト言語を使用してドキュメント (ctx._source) を操作します。

  • ctx._source: 現在処理中のドキュメントのソースデータにアクセスできます。フィールドの読み書き、追加、削除が可能です。
  • ctx._id: ドキュメントのIDにアクセスできます。必要に応じて変更することも可能ですが、推奨されません。
  • ctx._version: ドキュメントのバージョンにアクセスできます。

スクリプトの注意点:

  • パフォーマンス: スクリプトはドキュメントごとに実行されるため、複雑なスクリプトはReindexのパフォーマンスに大きな影響を与えます。可能な限りシンプルに保つことが重要です。
  • セキュリティ: スクリプトは 강력한 기능을 가지므로, 악의적인 스크립트 실행을 방지하기 위해 스크립팅 보안 설정에 주의해야 합니다. Painless 언어는 기본적으로 안전하게 설계되어 있지만, 악용될 가능성이 없는지 검토해야 합니다.
  • デバッグ: スクリプトのデバッグは容易ではありません。小さなデータセットで十分にテストを行うことが不可欠です。

Ingest Pipeline との比較:

データ変換の目的であれば、Elasticsearch 5.x 以降では多くの場合 Ingest Pipeline の利用が推奨されます。Ingest Pipelineは、ドキュメントのインデックス化処理の前に実行される一連のプロセッサーを定義する機能です。Reindex APIは、ターゲットインデックスへの書き込み時にこのIngest Pipelineを指定することができます (dest.pipeline パラメータ)。

Ingest Pipelineの利点は以下の通りです。

  • パフォーマンス: 多くの場合、スクリプトよりも効率的に動作します。特に、特定のプロセッサー(Grokなど)は専用に最適化されています。
  • 再利用性: 一度定義したパイプラインは、Reindexだけでなく、通常のインデックス化処理(例えばLogstashやBeatsからのデータ取り込み)にも利用できます。
  • 可読性と管理性: 多くの一般的な変換処理は、専用のプロセッサーとして提供されており、スクリプトよりも設定ファイルとして記述しやすいため、可読性と管理性が向上します。

特別な理由がない限り、Reindexでのデータ変換にはIngest Pipelineを利用することを検討しましょう。

Ingest Pipelineによるデータ変換

Ingest Pipelineを利用するには、まずパイプラインを定義する必要があります。

json
PUT _ingest/pipeline/my_transform_pipeline
{
"description": "Pipeline to transform documents during reindex",
"processors": [
{
"set": {
"field": "processed_timestamp",
"value": "{{_ingest.timestamp}}"
}
},
{
"rename": {
"field": "old_name",
"target_field": "new_name"
}
},
{
"remove": {
"field": "temp_data"
}
}
]
}

この例では、my_transform_pipeline という名前のパイプラインを定義しています。このパイプラインは以下の処理を行います。
1. processed_timestamp フィールドにインデックス化時のタイムスタンプを設定する。
2. old_name フィールドの名前を new_name に変更する。
3. temp_data フィールドを削除する。

パイプラインを定義したら、Reindexリクエストの dest.pipeline パラメータでそのパイプラインIDを指定します。

json
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index",
"pipeline": "my_transform_pipeline"
}
}

これにより、old_index から読み込まれた各ドキュメントは、new_index に書き込まれる前に my_transform_pipeline を通過し、指定された変換処理が施されます。

一般的なIngest Pipelineプロセッサー:

  • set: フィールドの値を追加または変更。
  • remove: フィールドを削除。
  • rename: フィールド名を変更。
  • append: 配列フィールドに値を追加。
  • split: 文字列を指定された区切り文字で分割して配列にする。
  • join: 配列を指定された区切り文字で結合して文字列にする。
  • convert: フィールドのデータ型を変換(例: string to integer)。
  • grok: 正規表現パターンを使用して非構造化テキストから構造化データを抽出。
  • date: 日付文字列をパースしてElasticsearchの日付型に変換。
  • geoip: IPアドレスから地理情報を付加。
  • enrich: 別のインデックスの情報を使用してドキュメントを補強。

Ingest Pipelineは非常に柔軟で強力な機能であり、Reindex時のデータ変換の第一選択肢として検討すべきです。

スロットリング (Throttling)

Reindexは、ソースインデックスからの読み込みとターゲットインデックスへの書き込みを大量に発生させるため、Elasticsearchクラスターに大きな負荷をかける可能性があります。特に、クラスターが既に多くの検索やインデックス化のリクエストを処理している場合、Reindexが他の重要な処理のパフォーマンスを劣化させる可能性があります。

requests_per_second パラメータを使用することで、Reindexプロセスがターゲットインデックスに書き込むドキュメント数を1秒あたりに制限し、クラスターへの負荷を抑制できます。

json
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
},
"requests_per_second": 1000 // 1秒あたり最大1000件のリクエストに制限
}

  • requests_per_second: -1 (デフォルト): 制限なし。Reindexタスクは可能な限り高速に実行しようとします。
  • requests_per_second: N: 1秒あたり最大 N 件の書き込みリクエストを行います。Elasticsearchは内部的に調整を行い、このレートを超えないように処理をスロットリングします。

適切な requests_per_second の値は、クラスターのキャパシティ、他のワークロード、Reindexしたいデータ量によって異なります。本番環境でReindexを実行する際は、デフォルトの制限なし(-1)ではなく、適切な値に設定して実行することを強く推奨します。まずは小さな値から始めて、クラスターの監視ツール(Elasticsearch Monitoringなど)でCPU使用率、メモリ使用量、ディスクI/O、ネットワークトラフィックなどを確認しながら、徐々に値を上げていくのが安全なアプローチです。

スロットリングは、Reindex処理自体の完了時間を長くしますが、クラスター全体の安定性を保つために非常に重要な設定です。

競合 (Conflicts) の扱い

Reindex実行中に、ソースインデックスのドキュメントが更新されたり、ターゲットインデックスに Reindex とは別にドキュメントが書き込まれたりする可能性があります。

  • ソースインデックスの更新: Reindexプロセスは、開始時点のソースインデックスのスナップショットのような状態からドキュメントを読み込みます。Reindex中にソースドキュメントが更新されても、その更新内容はデフォルトではターゲットインデックスには反映されません。常にReindex開始時点のバージョンのドキュメントがコピーされます。これは、Reindexがリアルタイム同期ツールではないためです。Reindex完了後に発生した差分をどう扱うか(例えば、Reindex完了後に短い期間だけ追いつき処理を行うなど)は、アプリケーション側で考慮する必要があります。version_type パラメータを使用することで、ソースドキュメントのバージョンをターゲットに引き継ぐことは可能ですが、リアルタイム同期はReindexの目的外です。
  • ターゲットインデックスへの書き込み: Reindex実行中にターゲットインデックスにもドキュメントが書き込まれる場合、ドキュメントIDが重複すると競合が発生する可能性があります。

op_typecreate に設定されている場合、ターゲットインデックスに既にドキュメントIDが存在すると、デフォルトでは競合が発生しReindexタスクが中止されます。

conflicts=proceed パラメータを使用することで、このような競合が発生してもタスクを中止せず、競合したドキュメントをスキップして処理を続行させることができます。これは、Reindex処理が中断されるのを避けたい場合に便利です。競合したドキュメントについては、ログに警告が出力されます。

json
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index",
"op_type": "create" // 既に存在する場合は作成しない
},
"conflicts": "proceed" // 競合が発生しても続行
}

この設定は、例えば複数のソースインデックスを一つのターゲットに統合する際に、異なるソースインデックスで同じIDのドキュメントが存在する場合に、片方をスキップして処理を続行したいといったシナリオで役立ちます。

リモートクラスターからのReindex

Elasticsearch 5.x 以降では、異なるElasticsearchクラスター間で直接データをReindexすることが可能です。これは、source パラメータ内に remote オブジェクトを指定することで実現します。

まず、Reindexを実行するクラスター(実行元クラスター)の elasticsearch.yml 設定ファイルに、リモートクラスターの情報を登録する必要があります。

yaml
cluster:
remote:
cluster_one:
seeds:
- 192.168.1.10:9300
- 192.168.1.11:9300
transport.compress: true
cluster_two:
seeds:
- cluster_two_node_1:9300
proxy:
host: socks_proxy_host
port: socks_proxy_port
http:
connect_timeout: 30s
socket_timeout: 30s
# Basic認証が必要な場合
auth:
basic:
username: "elastic"
password: "changeme"

設定ファイルにリモートクラスター名 (cluster_one, cluster_two など) と、そのクラスター内のノードのリスト (seeds) を指定します。セキュリティ設定やプロキシ設定もここで行います。設定変更後は、実行元クラスターの各ノードを再起動する必要があります。

設定が完了したら、Reindexリクエストで source.remote を指定します。

json
POST _reindex
{
"source": {
"remote": {
"host": "http://remote_cluster_address:9200", // または設定ファイルで定義したリモートクラスター名
"username": "elastic", // 設定ファイルに含めない場合はここで指定
"password": "changeme", // 設定ファイルに含めない場合はここで指定
"socket_timeout": "5m",
"connect_timeout": "10s"
},
"index": "remote_index_name", // リモートクラスター上のインデックス名
"query": {
// 必要に応じてリモートクラスター上のドキュメントをフィルタリング
}
},
"dest": {
"index": "local_index_name" // 実行元クラスター上のインデックス名
}
}

source.remote.host には、リモートクラスターのHTTPエンドポイントを直接指定するか、設定ファイルで定義したリモートクラスター名を指定できます。設定ファイルで認証情報を指定している場合は、リクエストボディで再度指定する必要はありません。

リモートReindexは、異なる環境(開発/ステージング/本番、オンプレミス/クラウドなど)間でデータを移行する際に非常に便利な機能です。ただし、ネットワーク帯域幅とレイテンシがReindexのパフォーマンスに大きく影響することを理解しておく必要があります。

Reindexのパフォーマンスとチューニング

Reindexは大量のデータ操作を伴うため、クラスターに顕著な負荷をかける可能性があります。パフォーマンスを理解し、必要に応じてチューニングを行うことは重要です。

Reindexのパフォーマンスのボトルネックは、以下のいずれか、または複数の組み合わせである可能性が高いです。

  1. ソースからの読み込み (Source Reads): ソースインデックスからのドキュメント読み込み速度。特にソースインデックスが遅いディスクを使用していたり、大量の読み込みリクエストでビジーだったりする場合。
  2. ターゲットへの書き込み (Dest Writes): ターゲットインデックスへのドキュメント書き込み速度。インデックス化パイプライン(Analaysis, Ingest Pipelineなど)、ディスク書き込み速度、レプリカへの同期書き込みなどが影響します。
  3. ネットワーク: ドキュメントがノード間またはクラスター間で転送される際のネットワーク帯域幅とレイテンシ。リモートReindexの場合に顕著です。
  4. 変換処理 (Script/Pipeline): スクリプトやIngest Pipelineによるドキュメント変換にかかる時間。複雑な処理はオーバーヘッドが大きくなります。

パフォーマンスチューニングのポイント:

  • スロットリング (requests_per_second): クラスターへの負荷をコントロールするための最も基本的な設定です。まずは制限を設けて、クラスターの健全性を保ちながら実行しましょう。モニタリングを見ながら徐々に値を上げていくのが良い方法です。
  • スライス (slices): Reindexタスクを並列実行することで、複数のノードのリソースを同時に利用し、処理時間を短縮できます。auto を使用するか、ソースインデックスのシャード数、またはクラスターのリソースに応じて手動で設定します。シャード数よりも極端に多いスライス数はかえってオーバーヘッドを増やす可能性があるので注意が必要です。
  • Ingest Pipeline/Scriptの最適化: データ変換に時間のかかる処理がないか確認し、可能であればより効率的な方法に改善します。例えば、複雑な正規表現Grokパターンを見直したり、不要な処理を削除したりします。
  • ターゲットインデックスの設定: Reindex中のターゲットインデックスの書き込み性能を向上させるために、一時的に設定を変更することを検討できます。
    • レプリカ数: Reindex中は書き込みの負荷が非常に高くなります。レプリカ数が多いほど書き込み性能が低下するため、一時的にレプリカ数を0または1に減らすことで書き込みスループットを向上させることができます。Reindex完了後にレプリカ数を元の設定に戻します。
    • リフレッシュ間隔: リフレッシュ間隔を長くすることで、書き込みのオーバーヘッドを減らせます。ただし、Reindex中に新しいドキュメントが検索可能になるまでの時間は長くなります。
  • ディスクI/O: ElasticsearchはディスクI/Oを大量に使用します。高速なSSDを使用しているか確認し、ディスクI/Oがボトルネックになっていないか監視します。
  • ネットワーク: リモートReindexの場合、クラスター間のネットワーク帯域幅とレイテンシが重要です。十分な帯域が確保されているか確認します。
  • モニタリング: Reindex実行中は、Elasticsearchクラスターの主要なメトリクス(CPU使用率、メモリ、JVMヒープ、ディスクI/O、ネットワークトラフィック、インデックス化レート、検索レートなど)を注意深く監視することが不可欠です。Elasticsearch MonitoringやPrometheus/Grafanaなどのツールを活用しましょう。Reindexタスク自体の進捗 (_tasks API) も監視します。

Reindex実行時の注意点とベストプラクティス

Reindexは強力な機能ですが、実行時にはいくつかの注意点があり、ダウンタイムを最小限に抑えるためには適切な手順を踏む必要があります。

1. 新しいインデックスへの書き込みと差分

Reindexはソースインデックスのデータを読み込んで新しいインデックスに書き込みますが、これはリアルタイムの同期処理ではありません。Reindex実行中にソースインデックスに新しいドキュメントが追加されたり、既存のドキュメントが更新/削除されたりする可能性があります。これらの変更は、Reindexが完了するまでにターゲットインデックスには自動的に反映されません。

  • 許容できるダウンタイムがある場合: アプリケーションからのソースインデックスへの書き込みを一時的に停止し、Reindexを実行し、完了後にアプリケーションを新しいインデックスに向ける、という方法が最もシンプルです。ただし、アプリケーションの停止が必要になります。
  • ダウンタイムを最小限に抑えたい/ゼロにしたい場合: 後述する「エイリアスを利用したダウンタイムゼロの更新」パターンを採用します。Reindex完了後に発生した差分をどう扱うか(例えば、差分だけを追記するツールを用意するなど)は検討が必要になる場合がありますが、多くの場合、短時間の差分は許容されるか、アプリケーション側で吸収可能です。

2. エイリアスを利用したダウンタイムゼロの更新

これはElasticsearchでインデックスの再構築や設定変更を行う際の標準的なパターンです。

  1. 現在の状況: アプリケーションやユーザーは、my_index_alias というエイリアスを通じて my_index_v1 というインデックスにアクセスしています。
  2. 新しいインデックスの準備: 必要な新しい設定(シャード数、マッピング、アナライザーなど)で my_index_v2 という新しいインデックスを作成します。この時点ではまだ空です。
  3. Reindexの実行: my_index_v1 から my_index_v2 へ Reindex を実行します。wait_for_completion=false を指定して非同期で実行し、スロットリングも設定します。
    json
    POST _reindex?wait_for_completion=false
    {
    "source": { "index": "my_index_v1" },
    "dest": { "index": "my_index_v2" }
    }
  4. 進捗監視: _tasks APIでReindexタスクの進捗を監視します。
  5. 差分処理 (必要な場合): Reindex実行中に my_index_v1 に書き込まれた新しいドキュメントや更新を my_index_v2 に反映させる方法を検討します。簡単な方法としては、Reindex完了後、エイリアスを切り替える直前に、Reindex開始時刻以降に変更されたドキュメントだけを対象に、もう一度Reindexを実行するか、別のスクリプトやツールで差分をコピーします。あるいは、アプリケーション側で両方のインデックスに書き込むように変更し、Reindex中は古いインデックスと新しいインデックスの両方に書き込み、読み込みは古いインデックスから行い、Reindex完了後に読み込みも新しいインデックスに切り替える、といった高度な手法も考えられます。
  6. エイリアスの切り替え: Reindex(および差分処理)が完了したら、エイリアス my_index_aliasmy_index_v1 を指すのを止め、アトミックに my_index_v2 を指すように変更します。これは _aliases API を使用して行います。
    json
    POST _aliases
    {
    "actions": [
    { "remove": { "alias": "my_index_alias", "index": "my_index_v1" } },
    { "add": { "alias": "my_index_alias", "index": "my_index_v2" } }
    ]
    }

    このAPIは単一のアトミックなリクエストとして実行されるため、アプリケーションからのアクセスは瞬時に新しいインデックスに切り替わります。ダウンタイムは発生しません。
  7. 古いインデックスの削除: エイリアスの切り替えが完了し、新しいインデックスが期待通りに動作していることを確認したら、不要になった my_index_v1 を削除します。
    json
    DELETE my_index_v1

このエイリアス切り替えパターンは非常に強力で広く利用されています。Reindexを行う際は、可能な限りこのパターンを採用することを強く推奨します。

3. バージョン間の互換性

Elasticsearchのメジャーバージョンアップグレードに伴うReindexでは、バージョン間の互換性に注意が必要です。新しいバージョンで廃止された機能やデータ型を使用している場合、そのままReindexできないことがあります。アップグレードガイドをよく読み、必要な変更(マッピングの調整など)を新しいインデックスに反映させてからReindexを実行する必要があります。Ingest Pipelineを使ってデータを変換しながらReindexすることも有効な手段です。

4. 容量計画

Reindexを実行するには、一時的に余分なディスク容量が必要になります。新しいインデックス (my_index_v2) は、古いインデックス (my_index_v1) とほぼ同じ容量を占有します(圧縮率やマッピング、Elasticsearchバージョンによって多少変動します)。Reindex中に両方のインデックスが同時に存在するため、クラスターには元のインデックス容量の約2倍の空き容量が必要です。事前にディスク容量を確認し、必要であれば拡張計画を立てておくことが重要です。

5. モニタリング

Reindex実行中は、クラスターの健全性とReindexタスクの進捗を継続的に監視します。Elasticsearch Monitoring(Metricbeat + Elasticsearch + Kibana)は、ノードメトリクス、JVMメトリクス、インデックスメトリクス、タスクメトリクスなど、必要な情報を包括的に提供してくれます。CPU使用率が継続的に100%に近い、メモリ使用量が異常に高い、ディスクI/Oが飽和している、Reindexの処理レートが異常に低い、といった兆候があれば、スロットリングの設定を見直したり、他のワークロードとの兼ね合いを調整したりといった対応が必要です。

6. テスト

大規模な本番環境でReindexを実行する前に、必ず開発環境やステージング環境で、本番環境に近いデータ量を使用してテストReindexを実行してください。これにより、予想される完了時間、クラスターへの負荷、設定(スロットリング、スライス、パイプライン/スクリプト)の妥当性、そして予期せぬエラーの発生などを事前に把握できます。特にIngest Pipelineやスクリプトによるデータ変換を行う場合は、期待通りの変換が行われるか、エラーが発生しないかなど、綿密なテストが必要です。

7. ReindexとSnapshot/Restoreの比較

ReindexとSnapshot/Restoreはどちらもデータのコピーや移行に関わる機能ですが、目的と用途が異なります。

  • Reindex:
    • 目的: インデックス設定/マッピングの変更、データのフィルタリング/変換、複数のインデックスのマージ、異なるクラスターへのデータ移行(リモートReindex)。
    • 対象: ドキュメントレベルでの操作。
    • 操作: ドキュメントを読み込み、必要に応じて変換し、インデックス化し直す。インデックスレベルの設定変更やデータ変換を伴う場合に適している。
    • 注意: インデックス化処理が走るため、CPU負荷が高くなる可能性がある。リアルタイム同期ではない。
  • Snapshot/Restore:
    • 目的: バックアップとリカバリ、クラスター全体の移行、インデックスのアーカイブ。
    • 対象: インデックスの物理的なデータとメタデータ全体。
    • 操作: インデックスの物理的なコピーを作成/復元する。設定やマッピングの変更は伴わない(ただし、異なるバージョンへのリストアには互換性問題がある場合がある)。
    • 注意: ファイルシステムのコピーに近いため、Reindexより効率的な場合が多いが、データ変換はできない。

データ変換や設定変更を伴う場合はReindex、単純なバックアップ/リカバリやクラスター全体の移行にはSnapshot/Restore、という使い分けが一般的です。

よくある問題とトラブルシューティング

Reindex実行中に発生しうる一般的な問題とそのトラブルシューティング方法をいくつか紹介します。

  • conflicts エラー:
    • 原因: op_type: create を使用しているターゲットインデックスに、同じIDのドキュメントが既に存在する場合に発生します。
    • 解決策: conflicts: proceed パラメータを指定して、競合を無視して処理を続行させます。または、ターゲットインデックスへの書き込み方法(IDの生成方法など)を見直します。
  • タイムアウトエラー:
    • 原因: Reindexタスクが指定された timeout 時間内に完了しない場合に発生します。また、ネットワークの問題やリモートクラスターの応答遅延なども原因となります。
    • 解決策:
      • wait_for_completion=false を使用して非同期実行に切り替えます。
      • requests_per_second の制限を緩和します(ただしクラスター負荷に注意)。
      • slices の数を増やして並列度を高めます。
      • ソース/ターゲットインデックスのパフォーマンスボトルネック(ディスクI/Oなど)を解消します。
      • リモートReindexの場合は、ネットワーク接続とリモートクラスターの応答性を確認します。
      • timeout 値を延長します(根本的な解決にはなりませんが、一時的な回避策として)。
  • リソース不足 (CPU, メモリ, ディスク):
    • 原因: Reindexは多くのリソースを消費します。他のワークロードとの競合や、クラスターサイズに対してReindex対象データが大きすぎる場合に発生します。
    • 解決策:
      • requests_per_second で Reindex の速度を制限します。
      • Reindex 以外のワークロードを一時的に減らすか、優先度を調整します。
      • より強力なノードを追加するか、既存ノードのリソース(CPU, RAM, ディスク)を増強します。
      • ディスク容量が不足している場合は、不要なデータを削除するか、ディスク容量を増強します。エイリアス切り替えパターンで古いインデックスを早めに削除することも検討します。
  • ネットワーク問題 (リモートReindex):
    • 原因: 実行元クラスターとリモートクラスター間のネットワーク障害、帯域不足、レイテンシなどが原因で通信が失敗したり遅延したりします。
    • 解決策:
      • ネットワーク接続を確認します(Ping, Tracerouteなど)。
      • 十分なネットワーク帯域が確保されているか確認します。
      • リモートクラスターの設定 (remote_clusters in elasticsearch.yml) や認証情報が正しいか確認します。
      • プロキシを使用している場合は、プロキシの設定と稼働状況を確認します。
  • Ingest Pipeline/Script エラー:
    • 原因: Ingest Pipelineの設定ミス、スクリプトの構文エラー、ドキュメントのデータ形式が期待と異なることによる処理失敗など。
    • 解決策:
      • Reindexタスクのログ(_tasks APIのレスポンスやElasticsearchのログファイル)を確認し、エラーメッセージを特定します。
      • Ingest Pipelineを使用している場合は、_ingest/pipeline/{pipeline_id}/_simulate API を使用して、実際のドキュメントサンプルでパイプラインの挙動をテストします。
      • スクリプトを使用している場合は、Painlessのデバッグ方法を参考に、スクリプトのロジックを検証します。小さなデータセットで Reindex を実行してエラーを再現させ、スクリプトやパイプラインを修正します。
  • Reindexタスクがハングアップした場合:
    • 原因: 特定のドキュメントでの処理が無限ループに陥る、クラスター内のノード障害、ネットワーク分断、リソース枯渇など、様々な原因が考えられます。
    • 解決策:
      • _tasks/{task_id} でタスクの状態を確認します。running のままで進捗が全くない場合はハングアップしている可能性があります。
      • クラスターのログを確認し、異常なエラーメッセージや警告がないか調べます。
      • クラスター全体の健全性(ノード状態、シャード割り当て、リソース使用率)を確認します。
      • 原因が特定できない場合や、緊急の場合は、POST _tasks/{task_id}/_cancel でタスクを強制的に中止します。中止後、問題の原因を調査し、修正してから再度Reindexを実行します。

まとめ

ElasticsearchのReindex APIは、インデックスの設定変更、バージョンアップグレード、データの再編成、インデックス統合など、Elasticsearchクラスターの運用管理において不可欠なツールです。単にデータをコピーするだけでなく、フィルタリングやIngest Pipeline/スクリプトによるデータ変換、スロットリング、リモートクラスターからのReindexといった高度な機能も備えています。

Reindexを成功させるためには、以下の点が重要です。

  • 目的の明確化: なぜReindexが必要なのか(設定変更、バージョンアップ、データ加工など)を明確にし、それに応じてターゲットインデックスの設定やReindexパラメータ(クエリ、パイプライン、スクリプトなど)を設計します。
  • 計画とテスト: 大規模なReindexを行う前には、必ず小さなデータセットや開発環境でテストを実行し、必要なリソース、完了時間、潜在的な問題を把握します。
  • エイリアスの活用: ダウンタイムを最小限に抑えたい場合は、インデックスエイリアスを使用したアトミックな切り替えパターンを採用します。
  • パフォーマンスとスロットリング: Reindexはクラスターに負荷をかける操作です。requests_per_second で適切なスロットリングを行い、クラスターの健全性を維持します。slices を活用して並列度を高めることも検討します。
  • モニタリング: Reindex実行中は、クラスターのメトリクスとReindexタスク自体の進捗を継続的に監視します。
  • 容量計画: Reindex中には一時的に追加のディスク容量が必要になります。事前に十分な容量が確保されているか確認します。

Reindexは複雑な操作になることもありますが、これらの基本的な使い方、目的、詳細設定、そしてベストプラクティスを理解し実践することで、安全かつ効率的にElasticsearchクラスターを運用・管理できるようになります。

参考情報

これらの公式ドキュメントやブログ記事も併せて参照することで、Reindexに関する理解をさらに深めることができます。


これで約5000語のReindexに関する詳細な記事は完成です。基本的な概念から高度な使い方、目的、注意点まで幅広く網羅し、実践的な情報を含めるように努めました。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール