第1回 Elastisearch 入門 インデックスを設計する際に知っておくべき事

elastic_search
284件のシェア(すこし話題の記事)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今回、第1回目の Elasticsearch 入門という事で、今回は「インデックスを設計する際に知っておくべき事」というテーマにしてみました。ここでのインデックスの設計とは RDB のデータベースとかテーブル、ビューの設計に当たるところです。

Elasticsearch は RDB など他のデータベスに比べ、その設計方法も結構独特です。(と言うか同じ事を実現するにしても色々な方法が用意されていて、さらにアプリケーション要件〜システムアーキテクチャ、運用面など広い範囲が関わってくる)RDB との比較も交え解説していきます。

Index で分けるか? Type で分けるか?

例えば、商品情報を保存するインデックスの設計を考えてみましょう。いわゆるRDBの設計で言うところのテーブル設計ですね。おそらくRDBではアプリケーション要件のみが、その設計の中心になるはずです。例えば、商品名や説明、価格情報などの基本情報、カテゴリマスターがあって、1つの商品は SKU の組み合わせを複数持っているとか。あとはそれらの情報をどの単位でテーブルに落とし込むか?といった感じでしょうか?

これを Elasticsearch で設計する場合、考慮するのはアプリケーション要件だけではないのです。説明する前に簡単に RDB との論理的な構成を比較してみると以下のような構成です。

Relational DB  ⇒ Databases ⇒ Tables ⇒ Rows      ⇒ Columns
Elasticsearch  ⇒ Indices   ⇒ Types  ⇒ Documents ⇒ Fields

簡単に説明すると1つの Elasticsearch クラスターは複数の Indices (Databases) を定義する事ができ、それぞれに複数の Types (Tables) を構成する事ができます。Types それぞれに複数の Documents (Rows) を保存でき、Document 毎に複数の Fields (Columns) を持っています。

データ量や更新頻度に対する考慮

この説明だけ見ると、Elasticsearch でも商品情報のインデックスの設計は、Type の設計をすれば良いように思いますが、まずデータ量やそのデータの更新速度要件がインデックスの設計に関わってきます。

Elasticsearch の Index は、物理的な要素である複数の Shards から構成されていて、その Shards を複数のノードに分散することで1つの Index に対するデータ量や書き込み速度を分散することができる仕組みになっています。また1つの Index に対して、書き込み可能な Shards の数は Index 作成後は変更できないため、(インデックス速度をスケールできるサーバー台数の上限が決まってしまう)設計時にはこれらのことを考慮して、将来的なデータ増、インデックス速度要件を確認して、例えば商品カテゴリごとに Types で分けるのか、Indices レベルで分けるのか検討する必要があります。(商品カテゴリごとに Indeces レベルで分ける設計にすることで、Shards の数も、商品カテゴリ x Shards となるため、将来的なデータ増や書き込み速度の改善に対応しやすくなる)

すべてのカテゴリを1つのインデックスで管理:

# データ増・書き込みの分散可能なサーバー台数は5台
/product/items   ⇒ 5 shards

カテゴリごとに1つのインデックスで管理:

# データ増・書き込みの分散可能なサーバー台数は15台
/book/items      ⇒ 5 shards
/pc/items        ⇒ 5 shards
/movie/items     ⇒ 5 shards

親子関連のデータは別のインデックスにはできない

親子関連のデータは、別のインデックスには作成できません。例えば、1商品情報に対する SKU の組み合わせ情報や、1商品に対するクチコミ情報などです。

これらの情報は、Type で分けで、親子関連のデータとしてインデックスするか、1つのドキュメント内に Nested Type 型のデータとしてインデックスするのかのどちらかです。

運用要件もインデックス設計にかかわる

次に、運用要件に関わる設計です。Elasticsearch にはトランザクションはありません。インデックスされ次第データは順次検索結果に反映されていきます。例えば、商品カテゴリ単位で瞬時に検索結果へ追加したり、除外したりといった要件では、Elasticsearch の Alias 機能が役に立ちます。Alias 機能は RDB の View に近いイメージの機能ですが、Types (Tables) レベルではなく Indices (Databases) に対して別名をつけられる機能です。複数の Index に同じ名前の Alias をつけることができるので、事前に新しいインデックスを作成し、公開したいタイミングでその情報を検索結果に反映することができるのです。こういった運用要件もインデックスの設計に関わってきます。

多言語対応するときの考え方色々

例えば、商品検索で英語と日本語、中国語など多言語に対応したいという要件がある場合、この場合もインデックスの設計に関わってくる要件になります。すべての言語で同じ言語処理(例えば CJK Bigram で言語処理するなど)で問題なければ、Document の属性に lang を用意して、検索時に固定で絞り込むロジックを入れたり、Type で分けても良いでしょう。ただ、言語ごとに異なる言語処理が必要な場合は、Index レベルで分けたほうが良いです。なぜなら言語処理のカスタマイズはIndex レベルで定義するからです。また、韓国語など異なる言語が追加された場合にも、他の言語に影響なく追加することができるようになります。

1つのインデックスですべての言語を管理:

# 言語処理は統一
# Document に lang フィールドを用意して絞り込み可能にする
/product/items

1つのインデックスで言語ごとに Type で管理:

# 言語処理は統一または、Mapping 定義でそれぞれの言語で異なるAnalyzerを指定して管理(ちょっと複雑)
/product/en
/product/ja

言語ごとにインデックスを管理:

# 言語処理のみ言語特有の設計にが可能で、Mapping 定義は統一できる
# 他の言語に言語処理変更に伴う影響がない
/product-en/items
/product-ja/items

データ・スキーマが異なるだけの場合はちょっと悩ましい。

データ・スキーマが異なる商品情報のインデックスの設計は、基本的には Type で分けるもよし、Index で分けるもよしです。(上記に挙げたようなデータ量によるスケールや運用要件がなければ)

Index 名の命名規約にも注意すべし

Index の名前のつけ方も、インデックスを設計する際に考慮する点が幾つかあるので紹介します。

Tribe Node を使うならすべてのクラスターで重複しないインデックス名で設計

いきなり Tribe Node と言う単語を出してしまいましたが、ノードの種類の1つで、検索のリクエストを複数のクラスターへ伝播する Proxy 的な役割を持った特殊なノードです。

例えば、Elasticsearch を複数のクラスターで運用する予定で、1つのエンドポイントで検索できる仕組みにしたいなどの要望がある場合、Tribe Node を使用するといった選択肢もあるのですが、Tribe Node は複数のクラスターで同じ名前のインデックスを処理できないため使用する可能性がある場合は、すべてのクラスターでインデックス名が重複しないように設計してください。

インデックス設定のテンプレート化のための考慮

Elasticsearch には、言語処理やマッピング定義、エイリアスの設定などテンプレート化する Index Template と言う機能があります。

テンプレートの適用対象はインデックス名で決定するため、インデックス名の命名規約の設計でも考慮しておいたほうが良いです。

以下のテンプレートは、product-ja や product-en の両方に適用されます。

{
    "template": "product*",
    ...
}

以下の Index Template は、product-ja のみに適用されます。

{
    "template": "*ja",
    ...
}

このようにインデックスの名称を使って、適用する設定を制御することができるので、インデックスの命名規約はインデックスを設計する際に決めておくと良いでしょう。

同じスキーマかつ同じ言語処理でも Index を複数作成するように設計する場合もある

「Index で分けるか? Type で分けるか?」でも説明しましたが、インデックスを作成する単位が、将来的な拡張や、書き込み処理における分散処理にも影響するので、同じデータスキーマで、全く同じ言語処理の場合でも、さらに Index を複数に分ける場合があります。

代表的なのは、アクセスログなどのインデックス化です。日に日にデータも増え、書き込み頻度も多い為、インデックスを日付や月単位で新しく作成してアクセスログをインデックス化していきます。こうすることで、Shard を増やすことができるので、サーバーを増やしてデータ増にも対応できますし、書き込み負荷も一定に保たれます。

少し変わった例を挙げると、Twitter のツイート情報のインデックス化では、アクセスログのように日付単位や月単位で複数インデックスを作成する設計もありますが、例えば会員サイトで、ユーザーごとにそのツイート情報を分析や検索などで使用したい場合などでは、同じツイート情報でも、インデックスをユーザー単位に作成することで、管理しやすくなります。

まとめ

いかがでしたでしょうか? Elasticsearch のインデックスの設計は、RDBのテーブル設計とは違い、様々な要素が関わってきます。アプリケーション要件、パフォーマンス要件、運用要件などが設計に関わってくるので難しいところもあるのですが、Elasticsearch は高度な検索機能を備えつつ、検索トラフィック量の増大とデータ量の増大(書き込み速度面も含む)の両方で高いスケーラビリティを発揮してくれます。インデックスの設計はその特徴を引き出すための1つの鍵です。特に大規模なシステムを設計する場合には今回説明した内容を思い出していただければと思います。