データベース

Elasticsearchのチューニング|メモリ配分とシャード設計・遅いクエリの特定手順

Elasticsearchのチューニング|メモリ配分とシャード設計・遅いクエリの特定手順

検索が遅い、投入が詰まる、ノードが落ちる。Elasticsearchの調整でまずいのは、症状を見ずに設定ファイルから触り始めることです。ヒープを増やして悪化する構成もあれば、広く知られた経験則が公式には退いている項目もあります。9.5系(2026年8月時点)の公式記述に沿って、メモリの配分、シャードの寸法、投入と検索それぞれの設定、そして遅いクエリを突き止める順序を、実装の解像度で整理しました。

まとめ:チューニング着手前に測る5つの値と判断

設定を変える前に、次の5点を数値で押さえてください。ここが埋まらないまま値をいじると、効いたかどうかの判定ができません。

  • ヒープの実効値:物理メモリの何%を割り当てているか、圧縮oopsが有効か
  • シャードの寸法:1シャードあたり何GB・何件で、1ノードに何本あるか
  • 投入の形:バルク1リクエストのサイズと、429が返っている割合
  • 検索の形:from+sizeの深さ、集計のカーディナリティ、キャッシュのヒット率
  • スローログ:そもそも有効になっているか(既定は無効)

結論から言えば、着手の順序はメモリ配分、シャードの寸法、投入と検索の設定、という並びが扱いやすくなります。ヒープは物理メモリの50%以下かつ26GB以下に収め、残り半分はファイルシステムキャッシュへ渡す。シャードは10GB〜50GBの幅に入れる。ここまでで直らない遅さだけが、クエリ単位の調整の対象です。着手前のサイジング逆算そのものはElasticsearchとは?転置インデックスとシャード設計で扱っているため、本記事は稼働したあとの測り直しだけを持ちます。

メモリ配分の原則|JVMヒープとページキャッシュを半分ずつに割る根拠

Elasticsearchのメモリは、JVMが管理するヒープと、OSがファイルを保持するページキャッシュの二つに割れています。この二つは奪い合う関係にあり、片方を伸ばすともう片方が痩せます。

ヒープ上限の二段の制約|物理メモリの50%と圧縮oopsの26GB

公式が示す上限は二段構えです。第一に、XmsとXmxは各ノードで使える総メモリの50%以下に設定します。この50%は推奨値というより、安全側の上限として意図された線です。第二に、圧縮ordinary object pointers(oops)の閾値以下に収めます。閾値は環境によって変わりますが、26GBならほぼ安全で、環境次第で30GBまで取れると記載されています。

二つのうち小さいほうが実際の上限になります。128GBのマシンでも64GBは割り当てず、26GB前後で止めるのが公式の読み方です。閾値を下回ったかどうかは、起動ログの記述で判定できます。

heap size [1.9gb], compressed ordinary object pointers [true]

この行で true が出ていれば圧縮oopsが効いています。false になっていたらヒープを取り過ぎで、同じメモリ量でも参照が太くなり、実効的な収容量が落ちます。設定は拡張子 .options のファイルを jvm.options.d ディレクトリへ置く形が公式の手順です。なお9.5系では、ノードのロールと総メモリからヒープサイズを自動決定する既定が入っており、公式は多くの本番環境で既定のサイジングを推奨しています。JVM側の用語やOutOfMemoryErrorの読み方はJavaヒープのメモリ構造とサイズ設定に整理しています。

残り半分をページキャッシュへ|Lucene がヒープ外で使う領域

50%という上限が置かれている理由は、残り半分をOSに渡すためです。Elasticsearchの索引実体はLuceneのセグメントファイルで、検索時にはこれをOSのページキャッシュ経由で読みます。公式は投入側でも検索側でも「システムメモリの少なくとも半分をファイルシステムキャッシュへ」と繰り返し書いています。

ここを取り違えると、ヒープを増やしたのに検索が遅くなる現象が起きます。ヒープに寄せた分だけキャッシュに載る索引が減り、ディスクからの読み出しが増えるためです。キャッシュが足りなければ古いデータから追い出され、追い出された領域を触るクエリだけが突然遅くなります。ストレージ側の読み出し性能がどこで頭打ちになるかはIOPSとスループット・レイテンシの関係と合わせて見ると切り分けが早く済みます。

サーキットブレーカーの既定値|95%で落ちる前に確認する三つの指標

ヒープが逼迫すると、Elasticsearchは操作を拒否して自衛します。この仕組みがサーキットブレーカーで、既定値は次のとおりです。

設定 既定値 役割
total.use_real_memory true 実メモリ基準で判定
total.limit ヒープの95% 親ブレーカーの上限
fielddata.limit ヒープの40% fielddataの上限
request.limit ヒープの60% 1リクエストの上限
inflight_requests.limit ヒープの100% 親の制限に従う

use_real_memory が false の場合、親ブレーカーの既定は70%へ変わります。ここでよくある誤りが、ブレーカーの上限を引き上げて拒否を止める対処です。拒否はヒープ不足の通知であって原因ではないため、上限を緩めるとノードごと落ちる側へ振れます。拒否が出たら、まず高カーディナリティの集計、深いページング、巨大なバルクの三つを疑ってください。ヒープ使用の推移とGCの挙動そのものを読む前提はJavaのGCの仕組みと種類にまとめています。

シャード設計の見直し|10GBから50GBの幅と8.3で退場した旧基準

シャードは分割の単位であると同時に、リソースを消費する実体です。本数が多すぎても少なすぎても遅くなるため、寸法の判断基準を先に固定します。

ヒープ1GBあたり20シャードという旧ルールを今も使うかの判断

日本語の解説記事で今も広く引かれているのが「ヒープ1GBあたり20シャード未満」という経験則です。Elastic自身のブログにもこの記述は残っています。ただしElasticsearch Labsの記述によれば、この経験則はElasticsearch 8.3で非推奨となり、フィールド密度に基づくサイジングへ置き換えられています。

したがって、8.3以降のクラスタを触るときにこの数字を第一基準へ置くのは避けてください。現行docsが示す基準は、シャードサイズ10GB〜50GB、1シャードあたり2億件未満、という寸法側の指標です。旧ルールに合わせて本数を絞り込むと、1本あたりが50GBを大きく超えて復旧やリバランスが遅くなる方向へ振れます。分割そのものの考え方を整理し直したい場合はシャーディングの仕組みとシャードキー設計を先に読むと判断が付けやすくなります。

1ノードあたり1,000シャードという上限と超えたあとの縮退手順

クラスタ側にも上限があります。非フローズンのシャードは1ノードあたり1,000本、専用フローズンノードでは3,000本を超えて作成できません。マスターノードのヒープは、インデックス3,000件あたり1GBで見積もる目安が示されています。

上限に近づいたときの縮退手段は四つです。似たマッピングのインデックスをreindex APIで1本へ統合する、書き込みが止まったインデックスへshrink index APIをかけて本数を減らす、ロールオーバーの閾値を引き上げる、オフピークにforce merge APIでセグメントを併合する。ロールオーバーの条件は max_age より max_primary_shard_size を優先するほうが、寸法の幅に収めやすくなります。ログ基盤で保持期間とロールオーバーを設計する話はELKスタックの収集経路と保持期間の設計で扱っています。

本数を後から変える三手段|reindexとshrinkとロールオーバー

プライマリシャードの本数は、作成後に直接変更できません。運用中に本数を変えるなら、次の順で判断すると手戻りが減ります。

  • 書き込みが続いている:ロールオーバー閾値を変え、次世代から新しい本数にする
  • 書き込みが止まった:shrink index APIで本数を減らし、force mergeで併合する
  • マッピングごと変えたい:reindex APIで別インデックスへ写し、エイリアスを切り替える

force merge は書き込み中のインデックスへかけてはいけない、と公式が明示しています。併合済みの巨大セグメントへ更新が入ると、削除マークが溜まって回収されない状態が続くためです。時系列データで書き込みが止まった世代だけを単一セグメントへ寄せる、という使い方に限定してください。

投入速度の調整|リフレッシュ間隔とレプリカとバルクサイズの三点

投入が詰まるときは、クエリではなく書き込み経路の設定を見ます。効く順に三つあります。

refresh_intervalの既定1秒と検索アイドルの30秒の関係

Elasticsearchは既定で1秒ごとにインデックスをリフレッシュします。ただし対象は、直近30秒に1回以上の検索リクエストを受けたインデックスだけです。この条件を知らないと、検索の来ていないインデックスで秒間リフレッシュのコストを見積もり、実態と合わない計算をしてしまいます。

投入が主で即時の可視性が要らないなら、間隔を延ばします。

PUT my-index-000001/_settings
{
  "index": {
    "refresh_interval": "30s"
  }
}

初期ロードのように検索が一切来ない区間では -1 にして完全に止め、投入後に元へ戻します。延ばした分だけ投入したドキュメントが検索に出るまでの遅延が伸びるため、業務要件の許容遅延から逆算して値を決めてください。可視化側で「入れたはずのデータが出ない」と見える場合、この設定が原因かどうかはKibanaのDev Toolsとデータビュー定義から確認すると切り分けが速く進みます。

初期ロード時に退避する二つの設定|レプリカ数とリフレッシュ間隔

大量の初期ロードでは、index.number_of_replicas を 0 にしてから投入し、完了後に戻す手順が公式に示されています。レプリカがある状態では同じ内容を複製先でも索引するため、単純に倍の作業が走ります。

ただしこの間はレプリカがないため、ノードが1台落ちるとデータを失います。実行するのは、元データが別にあって再投入できる初期ロードに限ってください。差分同期の運用に入ったあとで replicas を 0 に落とす判断は、可用性の要件と噛み合わなくなります。

投入バッファ側の既定は indices.memory.index_buffer_size が 10% です。公式が求めるのは、重い投入時にシャードあたり最大512MBの投入バッファを与えられる大きさです。1ノードで多数のシャードへ同時に書いている構成では、この10%が先に枯れます。

バルク1リクエストのサイズを実測で決める手順と429が出る意味

バルクのサイズに定数はありません。公式が示すのは実測の手順です。シングルノード・シングルシャードでベンチマークを組み、まず100件、次に200件、400件と倍にしていって、スループットが頭打ちになる点を探します。上限の目安は、1リクエストで数十MBを超えないことです。

送信は複数スレッド・複数プロセスから並行に行い、TOO_MANY_REQUESTS(429)が返り始めたら過負荷と判断します。429はクライアント側の失敗ではなく、クラスタが受けきれていない通知です。ここで並列度を上げると事態が悪化するため、並列度を落とすかノードを足す側で対処してください。送信側での分割と再送の組み立てはElasticsearchのデータ投入とbulk APIの部分失敗の扱いにまとめました。ドキュメントIDを自前で採番していない場合は、自動生成IDを使うと既存判定を省けるぶん投入が速く進みます。

検索速度の調整|キャッシュとページングとマッピングの三つの打ち手

メモリとシャードを揃えたあとに残る遅さは、クエリの形とマッピングに原因があります。効きやすい順に三つ挙げます。

深いページングの上限1万件|search_afterとPITへ寄せる

既定では from と size で1万件を超えてページングできません。この上限が置かれているのは、深いページや大きな結果集合ではメモリとCPUの使用が大きく増え、性能劣化やノード障害につながるためです。上限値を引き上げる設定は存在しますが、引き上げた分だけ落ちるリスクを引き受けることになります。

1万件を超えて辿る必要があるなら、search_after を使います。インデックスの状態を保ったまま辿るなら、search_after と point in time(PIT)を併用する形が現行の推奨です。スクロールAPIは深いページングにはもう推奨されていないため、既存実装に残っている場合は移行対象として扱ってください。

シャードリクエストキャッシュ|既定1%とリフレッシュで消える性質

集計主体のダッシュボードでは、シャードリクエストキャッシュが効きます。indices.requests.cache.size の既定はヒープの1%です。古い結果はインデックスのリフレッシュ時に自動で無効化されます。

この性質から導かれる判断が一つあります。リフレッシュ間隔が1秒のインデックスでは、キャッシュはほぼ毎秒捨てられ、ヒットしません。ダッシュボードの応答を上げたいなら、キャッシュサイズを増やす前にリフレッシュ間隔を伸ばせるかを先に見てください。値を伸ばす前に、集計側の期間やインターバルを見直す順序も併せて検討すると、変更点が少なく済みます。

preference に利用者やセッションを識別する値を渡すと、同じ利用者の反復リクエストが同じシャードコピーへ寄り、ノード側のキャッシュが効きやすくなります。集計のバケット化に多用する keyword フィールドには eager_global_ordinals を true にする手もありますが、ヒープ使用とリフレッシュ時間が増えるため、集計対象が固定されている場合に限る判断です。

識別子をkeyword型へ寄せる判断と、数値型のままでよい場面

範囲検索が要らない識別子は、数値型よりも keyword でマップするほうが term クエリは速いことが多い、と公式が書いています。ISBNや商品IDのように、等値でしか引かない値が該当します。

逆に、範囲での絞り込みや数値としての集計が要る値は数値型のまま置きます。判断がつかない場合はマルチフィールドで両方を持たせる形が示されていますが、フィールドが増えるとセグメントごとのヒープとディスクのオーバーヘッドが増えます。すべてを両持ちにするのではなく、実際に引かれているクエリを見てから決めてください。両持ちにする条件と片方で足りる場面の線引きはElasticsearchのマッピング設計|型選定とdynamicの制御、作り直しの手順にまとめています。

遅いクエリの特定手順|スローログから四段階で原因を絞り込む順序

ここまでは設定側の話です。個別のクエリが遅い場合は、測る順序を固定しておくと切り分けが短く済みます。

第一段:スローログを有効にする|既定の-1では何も出ない前提

最初の落とし穴が、スローログの閾値はすべて既定 -1 で無効という点です。有効にしない限り、遅いクエリはどこにも記録されません。検索側は query フェーズと fetch フェーズで別々に閾値を持ちます。

PUT my-index-000001/_settings
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.indexing.slowlog.threshold.index.warn": "10s"
}

閾値はシャード単位の所要時間で判定されます。クライアントから見た応答時間とは一致しないため、まず粗い値で有効にして、記録が出るところまで下げていく進め方が扱いやすいでしょう。include.user を true にすると user.* と auth.type がログへ入り、誰が投げたクエリかを追えます。ソースは既定で先頭1000文字が記録されます。

第二段:スレッドプールのrejectedを数えて詰まりの層を割る

次に見るのが、個々のクエリではなく処理の詰まりです。検索スレッドプールの拒否数を数えます。

GET _cat/thread_pool/search?v&h=node_name,name,active,rejected,completed

rejected が特定ノードだけで伸びている場合、そのノードにホットなシャードが偏っています。全ノードで一様に伸びているなら、クラスタ全体の容量不足です。ここで層が割れるため、クエリの書き換えに進むか、シャードの再配置やノード追加に進むかを決められます。同系列の製品であるOpenSearchでは同種の指標を専用プラグインから取れるため、比較検討の材料としてOpenSearch Performance Analyzerのメトリクス取得も参照できます。

第三段:プロファイルAPIで所要時間の内訳を処理段階ごとに割る

対象のクエリが絞れたら、プロファイルAPIで実行の内訳を見ます。リクエストへ profile を足すだけです。

POST my-index-000001/_search
{
  "profile": true,
  "query": { "match": { "message": "error" } }
}

返る内訳で、時間がクエリの評価に出ているのか、集計に出ているのか、フェッチに出ているのかが分かれます。評価側に出ているなら、ワイルドカードの前方一致やスクリプトの見直しが対象です。クエリの書き方そのものはElasticsearchのクエリDSLとboolの組み立てにまとめています。集計側なら、カーディナリティの高いフィールドをバケット化していないかを疑います。Kibanaには可視化されたプロファイラの画面があり、内訳をツリーで追えます。

第四段:hot_threadsでCPU時間の居場所を突き止める

ここまでで原因が定まらない場合、ノードのCPU時間がどこで消えているかを直接見ます。

GET _nodes/hot_threads

マージに時間が出ていればセグメント併合の負荷、GCに出ていればヒープ不足、検索スレッドに出ていればクエリ自体の重さ、と読み分けられます。タスク管理APIと組み合わせると、走り続けている長時間クエリを特定してキャンセルできます。障害中の応急処置としてはキャンセル、恒久対処としては前段のいずれか、という二段構えで扱ってください。

チューニングだけで回収できる条件と、着手前に確認する三つの前提

設定の見直しで戻せる遅さと、構成を組み直さないと戻らない遅さがあります。ここを見誤ると、効かない変更を繰り返す時間だけが積み上がります。

設定変更で戻せる条件|次の三つをすべて満たすなら調整で回収できる

次の三つが揃っているなら、設定の見直しで回収できる見込みが立ちます。

  • ヒープが物理メモリの50%以下かつ26GB以下で、圧縮oopsが true
  • 1シャードが10GB〜50GBの幅に入り、1ノードのシャードが1,000本未満
  • 遅いのが一部のクエリか一部の時間帯に限られ、全体が一様に遅いのではない

この状態なら、スローログを有効にして対象を絞り、プロファイルAPIで内訳を割り、クエリかマッピングを直す流れで収束します。コンテナで動かしている場合はホスト側のメモリ制限とES_JAVA_OPTSの関係も併せて確認が要るため、Docker構築時のメモリとヒープの決め方を参照してください。

見送る場面:設定では届かず構成の組み直しへ進むときの判断の線引き

逆に、次のいずれかに当たるなら設定の調整では戻りません。

  • ファイルシステムキャッシュへ渡せるメモリが索引サイズに対して桁で足りない
  • 1シャードが数百GBに膨らみ、復旧やリバランスが業務時間内に終わらない
  • 全ノードで一様に rejected が伸び、投入と検索が同じクラスタで競合している

一つ目と二つ目はノード追加かインデックス設計の作り直し、三つ目は投入系と検索系のクラスタ分離が対処です。いずれも稼働中の切り替え計画が要り、設定変更のような巻き戻しは効きません。判断と移行計画の作成、監視項目の設計まで含めて外部の手を入れるなら、保守運用・内製化支援で稼働中クラスタの調整からお引き受けしています。

よくある質問

ヒープを増やせば検索は速くなりますか?

速くなるとは限りません。ヒープを増やすとファイルシステムキャッシュへ渡せるメモリが減り、索引の読み出しがディスクへ落ちるためです。公式が示す上限は総メモリの50%以下かつ圧縮oopsの閾値以下で、26GBならほぼ安全とされています。まず現在のヒープがこの範囲に収まっているかを確認してください。

ヒープ1GBあたり20シャードという目安は今も有効ですか?

Elasticsearch Labsの記述では、この経験則は8.3で非推奨となり、フィールド密度ベースのサイジングへ置き換えられています。8.3以降のクラスタでは、シャードサイズ10GB〜50GB・1シャード2億件未満・1ノード1,000シャード未満という現行の指標を基準にしてください。

refresh_intervalを長くすると何が犠牲になりますか?

投入したドキュメントが検索結果へ現れるまでの遅延が伸びます。既定は1秒ですが、対象は直近30秒に検索を受けたインデックスだけです。業務側が許容できる遅延から逆算し、初期ロード中は -1 で止めて完了後に戻す使い分けが扱いやすくなります。

スローログに何も出ないのは遅いクエリがないからですか?

そうとは限りません。スローログの閾値はすべて既定 -1 で無効のため、有効化しないと記録自体が発生しません。まず粗い閾値で有効にし、記録が出る水準まで段階的に下げてください。

1万件を超えるページングはどう実装すればよいですか?

from と size では既定で1万件を超えられません。search_after を使い、インデックスの状態を保つ必要があるなら point in time(PIT)と併用します。スクロールAPIは深いページングにはもう推奨されていないため、新規実装では選ばないでください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  3. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動
  4. 2026.10.09 テックブログ スタディサプリの不正アクセスとメールアドレス3,687件|アカウント列挙を防ぐ実装
  5. 2026.10.08 コラム 雇用保険の適用拡大:2028年10月の週10時間以上への変更と、勤怠・労務システムで直す判定ロジック

RELATED POSTS 関連記事

目次