- Published on
Elasticsearch完全ガイド2025:検索エンジンからログ分析、ベクトル検索まで
- Authors

- Name
- Youngju Kim
- @fjvbn20031
TOC
1. Elasticsearchとは
Elasticsearchは、Apache Lucene基盤(きばん)の分散(ぶんさん)検索(けんさく)および分析(ぶんせき)エンジンです。2010年にShay Banonが最初(さいしょ)にリリースして以来(いらい)、全文検索(ぜんぶんけんさく)、ログ分析、メトリクス監視(かんし)、セキュリティ分析、そして最近(さいきん)ではベクトル検索(Vector Search)まで活用(かつよう)範囲(はんい)が拡大(かくだい)しています。
1.1 なぜElasticsearchなのか
従来(じゅうらい)のRDBMSでLIKE '%keyword%'クエリを実行(じっこう)すると、テーブル全体(ぜんたい)をフルスキャンする必要(ひつよう)があります。データが数億件(すうおくけん)になると応答(おうとう)時間(じかん)が数十秒(すうじゅうびょう)に達(たっ)する可能性(かのうせい)があります。Elasticsearchは転置(てんち)インデックス(Inverted Index)構造(こうぞう)を使用(しよう)してミリ秒(びょう)単位(たんい)の検索応答を提供(ていきょう)します。
主要(しゅよう)な特徴(とくちょう):
- 分散アーキテクチャ: 水平(すいへい)スケーリング(Scale-out)が可能(かのう)で数十TBのデータ処理(しょり)
- 準(じゅん)リアルタイム検索: 文書(ぶんしょ)インデックス後(ご)約(やく)1秒(びょう)以内(いない)に検索可能(Near Real-Time)
- スキーマレス: JSONドキュメントをそのままインデックス可能、動的(どうてき)マッピング対応(たいおう)
- RESTful API: すべての操作(そうさ)をHTTP/JSONで実行
- 豊富(ほうふ)なエコシステム: Kibana、Logstash、Beats、APMとの統合(とうごう)
1.2 Elasticsearch vs RDBMS比較
| 項目(こうもく) | RDBMS | Elasticsearch |
|---|---|---|
| データ単位 | Row | Document (JSON) |
| コレクション | Table | Index |
| カラム | Column | Field |
| スキーマ | 固定(こてい)スキーマ | 動的マッピング可能 |
| 検索方式(ほうしき) | B-Treeインデックス | 転置インデックス |
| 拡張(かくちょう)方式 | Scale-up中心 | Scale-out (Sharding) |
| トランザクション | ACID対応 | 非対応 |
| 主な用途(ようと) | OLTP | 検索、分析、ロギング |
1.3 バージョン履歴(りれき)
ES 1.x (2014) - 初期安定化
ES 2.x (2015) - Pipeline Aggregation導入
ES 5.x (2016) - Lucene 6、Painlessスクリプティング
ES 6.x (2017) - インデックスごとに単一タイプ
ES 7.x (2019) - タイプ廃止、アダプティブレプリカ選択
ES 8.x (2022) - セキュリティデフォルト有効化、kNNベクトル検索、NLP統合
2. 転置インデックスの仕組(しく)み
Elasticsearchの検索速度(そくど)の秘密(ひみつ)は転置インデックスにあります。一般的(いっぱんてき)なデータベースが文書(ぶんしょ)から単語(たんご)を探(さが)す(Forward Index)のに対(たい)し、転置インデックスは単語から文書を探します。
2.1 転置インデックス構造
3つの文書があると仮定(かてい)します:
Doc 1: "The quick brown fox"
Doc 2: "The quick brown dog"
Doc 3: "The lazy brown fox"
転置インデックスは以下(いか)のように構成(こうせい)されます:
Term | Document IDs
----------|-------------
the | [1, 2, 3]
quick | [1, 2]
brown | [1, 2, 3]
fox | [1, 3]
dog | [2]
lazy | [3]
「fox」を検索すると、転置インデックスからすぐにDoc 1、Doc 3を見(み)つけることができます。文書数(ぶんしょすう)がどれだけ多(おお)くても、単語(たんご)の検索(けんさく)はO(1)に近(ちか)いです。
2.2 Luceneセグメント構造
Elasticsearch内部(ないぶ)のLuceneはデータをセグメント(Segment)単位(たんい)で保存(ほぞん)します:
Index
└── Shard (Lucene Index)
├── Segment 0 (immutable)
│ ├── Inverted Index
│ ├── Stored Fields
│ ├── Doc Values
│ └── Term Vectors
├── Segment 1 (immutable)
├── Segment 2 (immutable)
└── Commit Point (segments_N)
重要(じゅうよう)なポイント:
- セグメントは一度(いちど)作成(さくせい)されると変更(へんこう)不可(ふか)(Immutable)
- 文書削除(さくじょ)時は実際(じっさい)には削除せず
.delファイルにマーク - バックグラウンドでセグメントマージ(Merge)が発生(はっせい)
- Refresh(デフォルト1秒)ごとに新しいセグメントが作成(さくせい)され検索可能になる
2.3 Doc ValuesとFielddata
転置インデックスはテキスト検索に最適化(さいてきか)されていますが、ソートや集約(しゅうやく)には適(てき)していません:
転置インデックス(検索用): Term → Document IDs
Doc Values(集約/ソート用): Document ID → Values
- Doc Values:
keyword、numeric、date、ip、geo_pointタイプで自動(じどう)有効(ゆうこう)化。ディスクベース。 - Fielddata:
textタイプのソート/集約時に使用。ヒープメモリ使用のため注意(ちゅうい)が必要。
3. マッピングとアナライザ
3.1 マッピング定義(ていぎ)
マッピングはインデックスのスキーマを定義します。各フィールドのタイプと分析方法(ほうほう)を指定(してい)します。
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"price": {
"type": "float"
},
"category": {
"type": "keyword"
},
"description": {
"type": "text"
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
},
"location": {
"type": "geo_point"
},
"tags": {
"type": "keyword"
},
"reviews": {
"type": "nested",
"properties": {
"author": { "type": "keyword" },
"rating": { "type": "integer" },
"comment": { "type": "text" }
}
}
}
}
}
3.2 主要(しゅよう)フィールドタイプ
| タイプ | 説明(せつめい) | 転置インデックス | Doc Values |
|---|---|---|---|
text | 全文検索用(アナライザ適用) | O | X (Fielddata) |
keyword | 完全一致(いっち)、ソート、集約 | O | O |
long/integer/float | 数値(すうち) | O (BKD Tree) | O |
date | 日付(ひづけ)/時刻(じこく) | O | O |
boolean | true/false | O | O |
geo_point | 緯度(いど)/経度(けいど) | O | O |
nested | ネスト対象(独立(どくりつ)文書) | O | O |
dense_vector | ベクトル(kNN検索) | X | X(別途(べっと)格納(かくのう)) |
3.3 アナライザパイプライン
アナライザは3つのステージで構成(こうせい)されます:
テキスト入力
│
▼
Character Filter(文字フィルタ)
│ - html_strip: HTMLタグ除去
│ - mapping: 文字置換
│ - pattern_replace: 正規表現置換
▼
Tokenizer(トークナイザ)
│ - standard: Unicode単語境界基盤
│ - whitespace: 空白基準分割
│ - ngram: N-gramトークン生成
│ - edge_ngram: プレフィックスベーストークン
│ - kuromoji_tokenizer: 日本語形態素解析
▼
Token Filter(トークンフィルタ)
│ - lowercase: 小文字変換
│ - stop: ストップワード除去
│ - synonym: 同義語処理
│ - stemmer: ステミング
│ - kuromoji_part_of_speech: 日本語品詞フィルタ
▼
トークンストリーム(Term)
3.4 日本語(にほんご)アナライザ設定(せってい)(Kuromoji)
PUT /japanese_index
{
"settings": {
"analysis": {
"tokenizer": {
"kuromoji_custom": {
"type": "kuromoji_tokenizer",
"mode": "search",
"discard_punctuation": true,
"user_dictionary_rules": [
"エラスティックサーチ,エラスティックサーチ,エラスティックサーチ,カスタム名詞"
]
}
},
"filter": {
"kuromoji_pos_filter": {
"type": "kuromoji_part_of_speech",
"stoptags": [
"助詞-格助詞",
"助詞-係助詞",
"助詞-終助詞"
]
}
},
"analyzer": {
"japanese_analyzer": {
"type": "custom",
"tokenizer": "kuromoji_custom",
"filter": [
"kuromoji_baseform",
"kuromoji_pos_filter",
"lowercase",
"cjk_width"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "japanese_analyzer"
}
}
}
}
3.5 Analyze APIで分析結果(けっか)を確認(かくにん)
POST /japanese_index/_analyze
{
"analyzer": "japanese_analyzer",
"text": "Elasticsearchは強力な検索エンジンです"
}
// 結果
{
"tokens": [
{ "token": "elasticsearch", "position": 0 },
{ "token": "強力", "position": 2 },
{ "token": "検索", "position": 4 },
{ "token": "エンジン", "position": 5 }
]
}
4. Query DSL完全(かんぜん)攻略(こうりゃく)
ElasticsearchのQuery DSLはJSON基盤の強力(きょうりょく)なクエリ言語(げんご)です。大きくQuery Context(関連度(かんれんど)スコア計算(けいさん))とFilter Context(Yes/No判定(はんてい)、キャッシング)に分(わ)かれます。
4.1 Match Query(全文検索)
// 基本match - アナライザでトークン化後に検索
GET /products/_search
{
"query": {
"match": {
"description": "強力な検索エンジン"
}
}
}
// match_phrase - 単語の順序まで一致
GET /products/_search
{
"query": {
"match_phrase": {
"description": {
"query": "検索エンジン",
"slop": 1
}
}
}
}
// multi_match - 複数フィールドで検索
GET /products/_search
{
"query": {
"multi_match": {
"query": "Elasticsearchガイド",
"fields": ["title^3", "description", "tags^2"],
"type": "best_fields"
}
}
}
4.2 Term Query(完全一致)
// term - keywordフィールドの完全一致
GET /products/_search
{
"query": {
"term": {
"category": "electronics"
}
}
}
// terms - 複数値のいずれかに一致
GET /products/_search
{
"query": {
"terms": {
"status": ["published", "pending"]
}
}
}
// range - 範囲検索
GET /products/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 500
}
}
}
}
// exists - フィールド存在確認
GET /products/_search
{
"query": {
"exists": {
"field": "discount"
}
}
}
4.3 Bool Query(複合(ふくごう)クエリ)
GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "description": "検索エンジン" } }
],
"must_not": [
{ "term": { "status": "deleted" } }
],
"should": [
{ "term": { "featured": true } },
{ "range": { "rating": { "gte": 4.5 } } }
],
"filter": [
{ "term": { "category": "software" } },
{ "range": { "price": { "lte": 1000 } } }
],
"minimum_should_match": 1
}
}
}
Bool Queryの各(かく)節(せつ)(Clause):
| 節 | スコア反映(はんえい) | キャッシュ | 用途(ようと) |
|---|---|---|---|
must | O | X | 必須(ひっす)一致+スコア計算 |
must_not | X | O | 必須不一致 |
should | O | X | 一致で加点(かてん) |
filter | X | O | 必須一致(スコア無関係(むかんけい)) |
4.4 Nested Query
GET /products/_search
{
"query": {
"nested": {
"path": "reviews",
"query": {
"bool": {
"must": [
{ "term": { "reviews.author": "john" } },
{ "range": { "reviews.rating": { "gte": 4 } } }
]
}
},
"inner_hits": {
"size": 3,
"highlight": {
"fields": {
"reviews.comment": {}
}
}
}
}
}
}
4.5 Function Score Query
GET /products/_search
{
"query": {
"function_score": {
"query": { "match": { "title": "elasticsearch" } },
"functions": [
{
"field_value_factor": {
"field": "popularity",
"modifier": "log1p",
"factor": 2
}
},
{
"gauss": {
"created_at": {
"origin": "now",
"scale": "30d",
"decay": 0.5
}
}
},
{
"filter": { "term": { "featured": true } },
"weight": 5
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
5. 集約(しゅうやく)(Aggregation)完全攻略
Elasticsearchの集約はSQLのGROUP BYに相当(そうとう)しますが、はるかに強力です。バケット集約、メトリック集約、パイプライン集約に分類(ぶんるい)されます。
5.1 Bucket Aggregation
// terms集約 - カテゴリ別文書数
GET /products/_search
{
"size": 0,
"aggs": {
"by_category": {
"terms": {
"field": "category",
"size": 20,
"order": { "_count": "desc" }
}
}
}
}
// date_histogram - 時間帯別集約
GET /logs/_search
{
"size": 0,
"aggs": {
"logs_per_hour": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "1h",
"time_zone": "Asia/Tokyo",
"min_doc_count": 0
}
}
}
}
// ネスト集約 - カテゴリ別平均価格
GET /products/_search
{
"size": 0,
"aggs": {
"by_category": {
"terms": { "field": "category" },
"aggs": {
"avg_price": {
"avg": { "field": "price" }
},
"price_stats": {
"stats": { "field": "price" }
}
}
}
}
}
5.2 Metric Aggregation
GET /products/_search
{
"size": 0,
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"max_price": { "max": { "field": "price" } },
"min_price": { "min": { "field": "price" } },
"total_sales": { "sum": { "field": "sales_count" } },
"unique_brands": { "cardinality": { "field": "brand" } },
"price_percentiles": {
"percentiles": {
"field": "price",
"percents": [25, 50, 75, 90, 99]
}
}
}
}
5.3 Pipeline Aggregation
GET /sales/_search
{
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"total_revenue": {
"sum": { "field": "revenue" }
}
}
},
"avg_monthly_revenue": {
"avg_bucket": {
"buckets_path": "monthly_sales>total_revenue"
}
},
"max_monthly_revenue": {
"max_bucket": {
"buckets_path": "monthly_sales>total_revenue"
}
}
}
}
6. ELKスタック(Logstash, Kibana, Beats)
6.1 ELKスタックアーキテクチャ
データソース 収集 処理/変換 格納 可視化
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ App Log │──────────│ Filebeat │──────│ Logstash │──────│ Elastic │──────│ Kibana │
│ Server │ │ Metricbt │ │ Pipeline │ │ search │ │Dashboard │
│ Docker │ │ Heartbt │ │ (Filter) │ │ Cluster │ │ Lens/Map │
│ K8s │ │ Packetbt │ │ │ │ │ │ Alerting │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
6.2 Logstashパイプライン
# /etc/logstash/conf.d/main.conf
input {
beats {
port => 5044
}
kafka {
bootstrap_servers => "kafka:9092"
topics => ["app-logs"]
codec => json
}
}
filter {
# Grokパターンで非構造化ログをパース
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:msg}"
}
}
# 日付パース
date {
match => ["timestamp", "ISO8601"]
target => "@timestamp"
}
# GeoIP変換
geoip {
source => "client_ip"
target => "geo"
}
# 条件付き処理
if [level] == "ERROR" {
mutate {
add_tag => ["alert"]
add_field => { "severity" => "high" }
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "logs-%{+YYYY.MM.dd}"
user => "elastic"
password => "changeme"
}
}
6.3 Filebeat設定
# /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/app/*.log
multiline:
pattern: '^\d{4}-\d{2}-\d{2}'
negate: true
match: after
- type: container
paths:
- /var/lib/docker/containers/*/*.log
processors:
- add_docker_metadata: ~
output.logstash:
hosts: ["logstash:5044"]
6.4 Kibanaの主要(しゅよう)機能(きのう)
- Discover: ログ検索、フィルタリング、時間(じかん)帯(たい)別(べつ)分布(ぶんぷ)
- Dashboard: 複数(ふくすう)のビジュアライゼーションをまとめてダッシュボード構成(こうせい)
- Lens: ドラッグ&ドロップ可視化(かしか)ビルダー
- Maps: 地理(ちり)データ可視化
- Alerting: 条件(じょうけん)ベースのアラート(Slack、Email、PagerDuty)
- APM: アプリケーションパフォーマンスモニタリング
- Security: SIEM機能、セキュリティイベント分析
- Dev Tools: コンソールから直接(ちょくせつ)API呼(よ)び出(だ)し
7. ベクトル検索とkNN
Elasticsearch 8.xからネイティブに内蔵(ないぞう)されたベクトル検索は、意味(いみ)ベースの検索(Semantic Search)を可能にします。
7.1 ベクトル検索とは
従来のキーワード検索は正確(せいかく)な単語一致(いっち)に依存(いぞん)します。「自動車(じどうしゃ)」を検索しても「車両(しゃりょう)」や「vehicle」は見(み)つかりません。ベクトル検索はテキストを高次元(こうじげん)ベクトルに変換(へんかん)して意味的(いみてき)な類似度(るいじど)を計算(けいさん)します。
"自動車" → [0.12, -0.34, 0.56, ..., 0.89] (768次元)
"車両" → [0.13, -0.32, 0.55, ..., 0.88] (類似ベクトル)
"果物" → [-0.45, 0.67, -0.12, ..., 0.23] (異なるベクトル)
7.2 kNN検索インデックス設定
PUT /semantic-search
{
"mappings": {
"properties": {
"title": { "type": "text" },
"content": { "type": "text" },
"content_vector": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 200
}
}
}
}
}
7.3 kNN検索の実行(じっこう)
// 純粋kNN検索
GET /semantic-search/_search
{
"knn": {
"field": "content_vector",
"query_vector": [0.12, -0.34, 0.56],
"k": 10,
"num_candidates": 100
}
}
// ハイブリッド検索(キーワード + kNN)
GET /semantic-search/_search
{
"query": {
"match": {
"content": "自動車 おすすめ"
}
},
"knn": {
"field": "content_vector",
"query_vector": [0.12, -0.34, 0.56],
"k": 10,
"num_candidates": 100,
"boost": 0.5
},
"size": 10
}
7.4 HNSWアルゴリズム
HNSW(Hierarchical Navigable Small World)はkNN検索に使用(しよう)される近似(きんじ)最近傍(さいきんぼう)(ANN)アルゴリズムです:
Layer 2 (sparse): A ──── B
│
Layer 1 (medium): A ── C ── B ── D
│ │ │ │
Layer 0 (dense): A-E-C-F-B-G-D-H
| パラメータ | 説明 | デフォルト | トレードオフ |
|---|---|---|---|
m | 各ノードの接続(せつぞく)数 | 16 | 高いほど正確だがメモリ増加 |
ef_construction | インデックス構築(こうちく)時の探索(たんさく)範囲 | 200 | 高いほど正確だがインデックス遅延 |
ef | 検索時の探索範囲 | 100 | 高いほど正確だが検索遅延 |
8. クラスタ運用(うんよう)と管理(かんり)
8.1 クラスタアーキテクチャ
┌─────────────────────────────────────────────────┐
│ Elasticsearch Cluster │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐│
│ │ Master Node │ │ Master Node │ │Master Node ││
│ │ (Elected) │ │ (Eligible) │ │(Eligible) ││
│ └─────────────┘ └─────────────┘ └────────────┘│
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐│
│ │ Data Node │ │ Data Node │ │ Data Node ││
│ │ (Hot Tier) │ │ (Hot Tier) │ │(Warm Tier) ││
│ │ SSD 1TB │ │ SSD 1TB │ │ HDD 4TB ││
│ └─────────────┘ └─────────────┘ └────────────┘│
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Ingest Node │ │ Coord Node │ │
│ │ (Pipeline) │ │ (Routing) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────┘
8.2 シャード戦略(せんりゃく)
PUT /logs-2025.03
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"routing.allocation.require.data": "hot"
}
}
シャードサイジングガイドライン:
- シャード1つあたり10-50GB推奨(すいしょう)
- ノードあたりのシャード数:ヒープ1GBあたり20個以下
- 時系列(じけいれつ)データは日付ベースのインデックスを使用
8.3 Index Lifecycle Management (ILM)
PUT /_ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_primary_shard_size": "50gb",
"max_age": "1d"
},
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 },
"allocate": { "require": { "data": "warm" } },
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"allocate": { "require": { "data": "cold" } },
"freeze": {}
}
},
"delete": {
"min_age": "90d",
"actions": { "delete": {} }
}
}
}
}
8.4 クラスタ監視(かんし)API
# クラスタ状態
GET /_cluster/health
# ノード統計
GET /_nodes/stats
# インデックス状態
GET /_cat/indices?v&s=store.size:desc
# シャード割り当て状態
GET /_cat/shards?v&s=store:desc
# 割り当て失敗原因
GET /_cluster/allocation/explain
9. パフォーマンス最適化(さいてきか)
9.1 インデックスパフォーマンス
// Bulk API(個別リクエストの10倍以上高速)
POST /_bulk
{"index": {"_index": "products", "_id": "1"}}
{"name": "Product 1", "price": 100}
{"index": {"_index": "products", "_id": "2"}}
{"name": "Product 2", "price": 200}
インデックス最適化チェックリスト:
| 項目 | 設定 | 効果(こうか) |
|---|---|---|
| Bulkサイズ | リクエストあたり5-15MB | ネットワークオーバーヘッド削減 |
| Refresh Interval | "30s"または"-1" | セグメント生成頻度(ひんど)削減 |
| レプリカ数 | 初期ロード時0 | レプリケーションオーバーヘッド除去(じょきょ) |
| Translog | "async"フラッシュ | ディスクI/O削減 |
| ID生成 | 自動生成ID使用 | ID重複(じゅうふく)チェック省略(しょうりゃく) |
9.2 検索パフォーマンス
// 1. Filter Context活用(キャッシュされる)
GET /products/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "category": "electronics" } },
{ "range": { "price": { "gte": 100, "lte": 500 } } }
],
"must": [
{ "match": { "description": "wireless" } }
]
}
}
}
// 2. Source Filtering(必要なフィールドのみ)
GET /products/_search
{
"_source": ["name", "price", "category"],
"query": { "match_all": {} }
}
// 3. Search After(Deep Paginationの代替)
GET /products/_search
{
"size": 20,
"sort": [
{ "created_at": "desc" },
{ "_id": "asc" }
],
"search_after": ["2025-03-01T00:00:00", "abc123"]
}
9.3 キャッシュ戦略
Elasticsearchキャッシュ階層:
1. Node Query Cache(Filter Cache)
- filter contextの結果をキャッシュ
- ノードレベル、ヒープの10%(デフォルト)
- LRU方式除去
2. Shard Request Cache
- 集約結果をキャッシュ
- シャードレベル、ヒープの1%(デフォルト)
- インデックスrefresh時に無効化
3. OS Page Cache
- Luceneセグメントファイルのキャッシュ
- ヒープ外メモリを活用
- 最も重要なキャッシュ!
10. 実践(じっせん)運用パターン
10.1 Index TemplateとComponent Template
// Component Template(再利用可能なブロック)
PUT /_component_template/base-settings
{
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"index.lifecycle.name": "logs-policy"
}
}
}
PUT /_component_template/log-mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" },
"level": { "type": "keyword" },
"service": { "type": "keyword" },
"trace_id": { "type": "keyword" }
}
}
}
}
// Index Template
PUT /_index_template/logs
{
"index_patterns": ["logs-*"],
"composed_of": ["base-settings", "log-mappings"],
"priority": 200
}
10.2 AliasとReindex
// Alias設定(無停止インデックス切り替え)
POST /_aliases
{
"actions": [
{ "remove": { "index": "products-v1", "alias": "products" } },
{ "add": { "index": "products-v2", "alias": "products" } }
]
}
// Reindex(インデックスマイグレーション)
POST /_reindex
{
"source": {
"index": "old-index",
"query": {
"range": {
"@timestamp": { "gte": "2025-01-01" }
}
}
},
"dest": {
"index": "new-index",
"pipeline": "enrichment-pipeline"
}
}
10.3 SnapshotとRestore
// リポジトリ登録(S3)
PUT /_snapshot/s3-backup
{
"type": "s3",
"settings": {
"bucket": "my-es-backups",
"region": "ap-northeast-1",
"base_path": "elasticsearch"
}
}
// スナップショット作成
PUT /_snapshot/s3-backup/snapshot-2025-03-24
{
"indices": "logs-*,products",
"ignore_unavailable": true,
"include_global_state": false
}
// 復元
POST /_snapshot/s3-backup/snapshot-2025-03-24/_restore
{
"indices": "products",
"rename_pattern": "(.+)",
"rename_replacement": "restored-$1"
}
11. 面接対策(めんせつたいさく)クイズ
Q1. Elasticsearchで文書をインデックスした後すぐに検索できない理由は?
ElasticsearchはNear Real-Time(NRT)検索エンジンです。文書がインデックスされると、まずメモリバッファ(In-Memory Buffer)とトランザクションログ(Translog)に記録(きろく)されます。検索可能にするにはRefresh処理(しょり)を通(つう)じてメモリバッファの内容(ないよう)が新しいLuceneセグメントとして生成(せいせい)される必要があります。
デフォルトのRefresh間隔(かんかく)は1秒で、これが「Near Real-Time」と呼(よ)ばれる理由(りゆう)です。
index.refresh_interval設定で調整(ちょうせい)可能- 即時(そくじ)検索が必要な場合は
POST /index/_refreshAPI呼び出し - 大量インデックス時は
-1に設定して無効化後、完了後に手動(しゅどう)refresh
Q2. textタイプとkeywordタイプの違(ちが)いは?
textタイプ:
- アナライザでトークン化される
- 転置インデックスに個別(こべつ)トークンで格納
- 全文検索(Full-Text Search)に使用(match query)
- Doc Values非対応(ソート/集約時はFielddata使用、メモリ注意)
keywordタイプ:
- 分析(ぶんせき)なしで原文(げんぶん)のまま格納
- 完全一致検索に使用(term query)
- ソート、集約、フィルタリングに最適化
- Doc Values対応(ディスクベース)
実務(じつむ)のヒント: nameのようなフィールドはMulti-fieldで両方(りょうほう)のタイプを設定するのが一般的(いっぱんてき)です。
Q3. Bool Queryのmustとfilterの違いは?
どちらも「必ず一致(いっち)しなければならない」条件ですが、重要(じゅうよう)な違いがあります:
must:
- 関連度スコア(_score)を計算
- キャッシュされない
- 「どの程度(ていど)一致するか」が重要な場合に使用
filter:
- スコアを計算しない(0点)
- 結果がキャッシュされる(Node Query Cache)
- 「一致するか/しないか」だけが重要な場合に使用
最適化の原則(げんそく): スコアが不要な条件はすべてfilterに移動(いどう)してください。
Q4. Primary ShardとReplica Shardの役割(やくわり)は?
Primary Shard:
- インデックスデータの原本(げんぽん)を格納
- インデックス作成後に数(かず)を変更不可(shrink/split除外(じょがい))
- すべてのWrite要求はまずPrimary Shardで処理
- データ分散の単位
Replica Shard:
- Primary Shardのコピー
- 動的に数を変更可能
- 2つの役割:
- 高可用性(こうかようせい): Primary Shard障害(しょうがい)時にReplicaが昇格(しょうかく)
- 検索性能(せいのう): 検索リクエストを分散処理(Read負荷(ふか)分散)
- Primaryと同じノードには配置(はいち)されない
Q5. ElasticsearchでDeep Paginationが危険な理由と代替案(だいたいあん)は?
Deep Paginationの問題:
from: 10000, size: 10のリクエスト時、各シャードで10,010件の文書をソートしてCoordinating Nodeに送信(そうしん)する必要があります。3つのシャードなら30,030件をメモリでソートしなければなりません。ページが深(ふか)くなるほどリソース消費(しょうひ)が指数関数的(しすうかんすうてき)に増加します。
代替案:
- Search After: 前のページの最後のソート値を基準(きじゅん)に次のページを取得。リアルタイムカーソルベース。
- Point in Time (PIT): Scrollの代替。search_afterと組み合わせて一貫(いっかん)したビューを提供。
// PIT + Search After例
POST /products/_pit?keep_alive=5m
GET /_search
{
"pit": { "id": "PIT_ID", "keep_alive": "5m" },
"size": 20,
"sort": [{ "created_at": "desc" }, { "_shard_doc": "asc" }],
"search_after": [1679616000000, 42]
}