ElasticsearchをNoSQLデータベースとして使う
更新:本記事で、マネージドのElasticsearchは旧称のFoundで表記されています。Foundの現在の名称はElastic Cloudですのでご注意ください。Elastic Cloudでは、マネージドのElasticsearchを14日間無料でお試しいただけます。
Elasticsearchは、"NoSQL"データベースとして使うことができるのでしょうか?"NoSQL"は文脈によって意味が変わる言葉ですし、興味深いことに実際にはSQLとは無関係だったりもします。ここでは"あいまい"なところから出発して、Elasticsearchのさまざまな特性だけでなく、最も柔軟かつスケーラブルでパフォーマンスに優れた検索および分析エンジンの1つとするために犠牲にした部分についても検討します。
そもそもNoSQLデータベースとは
NoSQL-Database.orgによると、NoSQLの定義は、「非リレーショナル、分散型、オープンソース、水平拡張可能の3点のいくつかを相当程度満たしている次世代データベース」です。つまり、あまり明確な定義とは言えません。
NoSQLかどうかは、特にSQLとの関わりの有無とは関係ありません。たとえば、Hiveのクエリ言語は明らかにSQLから着想を得ています。これはEsperのクエリ言語にも当てはまります(ただし、この言語はリレーションではなくストリームに動作します)。また、PostgreSQLはかつて"Postgres"と呼ばれ、クエリ言語が"Quel"だったことをご存じでしょうか。PostgreSQLは最初のORDBMSでしたが、いまでは多くの機能も搭載してスキーマレスなドキュメントストアとして利用可能なものとなっています。
また、NoSQLは、ACID特性を具備しているかどうかとも関係ありません。Hyperdexは、ACIDトランザクションを提供することを目的としたNoSQLデータベースの一例です。これに対して、間違いなくSQLデータベースのはずのMySQLには、ACIDの意味を怪しげに解釈してきた歴史があります。
リレーションという点はどうでしょうか。NoSQLデータベースの多くは、従来のリレーショナルデータベースと同じ意味での結合をサポートしておらず、ユーザーの実践に任せていますが、中には結合をサポートするものもあります。いくつか例を挙げると、RethinkDB、Hive、Pigは結合をサポートします。グラフ指向データベースであるNeo4jは、確かにリレーションを処理することができ、グラフのリレーションシップ(つまりエッジ)のトラバースに優れています。Elasticsearchには、親子関係を使用した"クエリ時"結合や入れ子になった型を使用した"インデックス時"結合といった概念があります。
分散についてはどうでしょう。分散型のSQLデータベースも存在しますし、NoSQLiteのようなものを目指しているプロジェクトもいくつかありますが、新しい世代のデータベースほど何らかの点で分散型になる傾向があります。
要約すれば、NoSQLを厳密に定義することにはあまり意味がないし、Elasticsearchのことを"ドキュメントストア"型のNoSQLデータベースであると言い切ることもできない、ということなのです。このブログの執筆時点で、nosql-database.orgには20を超えるNoSQLデータベースがリストされています。
次のセクションでは、いくつかの重要な特性について確認し、Elasticsearchがそれらをどのように実装しているか、あるいはしていないかを見ていきます。
トランザクションなし
Elasticsearchが基盤としているLuceneには、トランザクションの概念があります。一方、Elasticsearchには、通常の意味でのトランザクションはありません。送信したドキュメントをロールバックする手段はありませんし、まとめて送信したドキュメントのすべてにインデックスすることも、まったくインデックスしないでおくこともできません。ただし、Elasticsearchはログ先行書き込みを実装しています。これにより、負荷の大きいLuceneコミットを実行せずに操作の永続性を確保することができます。また、インデックス操作の一貫性レベルを指定することもできます。これは、いくつのレプリカが操作を承認すると結果が返されるかという観点から指定します。デフォルトはクォーラム、すなわち\(\lfloor\frac{n}{2}\rfloor + 1\)となります。
変更の可視性は、インデックスの更新時に制御されます(デフォルトは1秒に1回、シャードごとです)。
オプティミスティック同時実行制御は、送信されたドキュメントのバージョンを指定することによって実行されます。
Elasticsearchは速度重視で構築されています。分散トランザクションの実行には大きな負荷がかかりますが、これを提供しないことで、多くの部分を簡素化できます。Elasticsearchは、今読んでいるのが多少古くとも、誰もが同じタイムラインを見ているという状態を容認することで、キャッシュから多くのデータを提供できます。これは、最重要視している驚異的なパフォーマンスを実現するうえで非常に重要な点です。
スキーマの柔軟性
Elasticsearchでは、事前のスキーマ指定が必須ではありません。JSONドキュメントを投入すると、自動で型を推測するようになっているからです。数値、ブール、タイムスタンプなどの型については適切に動作します。文字列型の場合には、"スタンダード"アナライザーを使います。こちらも通常、使い始めなら十分満足できる水準です。
Elasticsearchは、スキーマの指定が必須ではないという意味では確かに"スキーマ不要"とも言いうるのですが、Elasticではむしろ"スキーマに柔軟性がある"という考え方をしています。優れた検索機能や分析機能を開発するためには、スキーマの微調整が必要になるのが現実です。Elasticsearchには、動的テンプレートやマルチフィールドオブジェクトなど、開発者をサポートする強力なツールが豊富に用意されています。この詳細については、マッピングに関するElasticの記事を参照してください。
リレーションと制約
Elasticsearchはドキュメント指向データベースです。検索対象のオブジェクトグラフ全体にインデックスを作成する必要があるため、ドキュメントにインデックスを作成する前に非正規化を行わなければなりません。非正規化によって(クエリの結合が不要になるため)取得のパフォーマンスが向上する一方、(複数回の格納が必要になるため)使用容量が増え、(すべての変更をすべてのインスタンスに適用する必要があるため)一貫性と最新状態の維持がより困難になります。ただし、追記型のワークロードには非常に適しています。
たとえば、顧客、注文、製品を含むデータベースをセットアップし、製品名とユーザーを指定して注文を検索するとします。これは、ユーザーと製品に関するすべての必要な情報を使用して注文にインデックスを作成することで解決できます。これだと検索は簡単ですが、製品名の変更が必要になったときはどうでしょうか。適切に正規化したリレーショナル設計では、単に製品を更新するだけで済みます。リレーショナルデータベースはこの点が非常に優れています。非正規化されたドキュメントデータベースでは、製品が含まれるすべての注文を更新しなければなりません。
言い換えると、Elasticsearchのようなドキュメント指向データベースでは、マッピングの設計やドキュメントの格納のあり方を、検索や取得に最適化していると言えます。
この記事の導入部で触れたように、Elasticsearchには、親子関係を使用した"クエリ時"結合や入れ子になった型を使用した"インデックス時"結合といった概念があります。この点については、今後の記事で詳しく説明するつもりです。ところで、Martijn van Groningenによる「Document relations with Elasticsearch(Elasticsearchを使ったドキュメントの関連付け)」というプレゼンテーションはお勧めです。
大部分のリレーショナルデータベースでは、制約を指定して、一貫性を保持するものとしないものを定義できます。たとえば、参照の整合性と一意性を適用できます。アカウントの移動の合計は正数でなければならない、などと要求することができます。ドキュメント指向データベースはこれを指定できないことが多く、Elasticsearchも例外ではありません。
堅牢性
データベースには堅牢性が求められます。組織の正式なレコードシステムである場合はなおさらです。負荷の大きなクエリはキャンセルできる必要がありますし、命令しない限りデータベースの動作は停止しないのが理想です。
残念ながら、Elasticsearch(およびその構成要素)は、現在のところ、OutOfMemory
エラーの処理が得意とはあまり言えません。この点については、「Elasticsearch in Production, OutOfMemory-Caused Crashes(Elasticsearchの実際、メモリー不足が原因のクラッシュ)」で詳しく取り上げています。Elasticsearchに十分な容量のメモリーを用意するとともに、運用クラスターでメモリー所要量がわからない検索を実行する際には十分注意することが重要です。
この点はElasticsearchの成熟とともに改善されていく見込みですが、Elasticsearchが十分なメモリーが提供されていることを前提に、速度優先で構築されていることは心に留めておいてください。
分散設計
Elasticsearchのネットワークについても参照してください。
Elasticsearchを開発する前、Shay BanonはCompassに取り組んでいました。Compassを分散型検索エンジンに転換することは困難だと悟った彼は、ゼロからElasticsearchの開発を開始しました1。Elasticsearchは、汎用ハードウェアで大量のデータを処理できるように、分散型でスケールアウトしやすい設計になっています。
Elasticsearchは、分散型システムとして使い始める段階では驚くほど容易ですが、分散型システムというのは複雑なものです。この点はElasticsearchのネットワークに関する情報を参照してください。以下は概要となります。
分散型システムは、その性質上、問題発生の原因が無数にあります。このように、データベースシステムが異なれば重点を置く強みも異なります。強力な保証を目指すシステムもあれば、時々(またはしょっちゅう)エラーが発生するにもかかわらず、常に利用可能であることを重視するシステムもあります。さらに、ネットワークパーティションの危険性に関する優れたシリーズにおいてKyle Kingsbury氏が説明しているように、あるデータベースシステムが特定の問題に対処できると謳っていても、実際に対処できることはほとんどありません。つまり、分散型データベースは平常時であれば正常に機能しますが、発生しうる膨大な量の障害のいずれかにさらされた場合、困難は避けられません。
一貫性(C)、可用性(A)、分断耐性(P)の観点から言えば、ElasticsearchはCPシステムです(ただし、"一貫性"にはかなり緩やかな定義を採用しています)。読み取り専用ワークロードの場合、Elasticsearchは"最小マスターノード"の要件を緩和する(つまり、クォーラムを必要としない)ことで、AP動作を実現できます。ただし、一般的にクラスター内の過半数のノードが利用可能であることが必要です。この過半数のノードを利用できるとは限らない誤った構成のクラスター("スプリットブレイン"のクラスター)に書き込んだ場合、回復不能なデータ損失が発生する可能性があります。これは何もElasticsearchに限ったことではありません。
スケーリングについては、インデックスを1つ以上のシャードに分割します。これは、インデックスの作成時に指定するため、あとで変更することはできません。そのため、予想される拡大に見合うようインデックスをシャード化する必要があります。そうしておくことで、Elasticsearchクラスターにノードを追加する際に、シャードの再配分や移動がうまくいきます。このように、Elasticsearchではスケールアウトが非常に簡単です。
セキュリティ
Elasticsearchのセキュリティについても参照してください。
Elasticsearchは、認証や承認の機能を備えていません。特にElasticsearchの強力なスクリプト機能を有効にする場合は、Elasticsearchクラスターに接続できる人には"スーパーユーザー"権限があると考える必要があります。
まとめ
説明したような制限が致命的な問題とはならない場合、Elasticsearchをプライマリストアとして使うことは確かに可能です。わかりやすい例として、Logstashを使用する場合が挙げられます。Logstashは大変便利なツールで、ログの管理、Elasticsearchへのログの格納、万一に備えて別の場所へのアーカイブなどを行うことができます。ログは一度書き込んだら何度も読み取られます。更新されませんし、トランザクションや整合性制約なども必要ありません。
では、全文検索やACIDトランザクションを伴うPostgresなどのシステムはどうでしょうか(他の例として、MySQL、MongoDB、Riakなどの全文検索機能を挙げることもできます)。 Postgresで基本的な検索を実装することはできますが、可能なパフォーマンスと機能の両方に大きなギャップが発生します。トランザクションに関するセクションで触れたように、Elasticsearchは複数バージョンの同時実行管理などの複雑な条件を心配することなく、"迂回策"や大量のキャッシュを使用することができます。また、検索はテキストの断片内でキーワードを見つけるというだけのことではありません。業界固有の知識を適用して適切な関連性モデルを実装したり、結果全体に関する概要を提供したり、入力時のスペルチェックやオートコンプリートを実行したりもします。さらに、速度も重要です。
Elasticsearchは一般的に、他のデータベースへの追加機能として使用されます。制約、正確さ、堅牢さ、そしてトランザクション方式でいつでも更新できることに重点を置いているデータベースシステムの場合、マスターレコードがあり、非同期的にElasticsearchにプッシュされます(Elasticsearchの"リバー"のいずれかを使用している場合はプルされます)。 同期の維持というテーマについては、今後の記事で詳しく説明したいと思います。Foundでは、通常、データ保持の機能としてPostgreSQLとZookeeperを使用します。このデータをElasticsearchに取り込むことで優れた検索を実現します。
すべてについて言えることですが、万能薬というものは存在しません。すべてを解決できるデータベースも存在しないのです。これは常に真実ですので、使用するストアの強みと弱みを把握しておく必要があります。
参照資料
Banon, Shay:The future of compass & elasticSearch(CompassとElasticsearchの今後) – https://thedudeabides.com/articles/the_future_of_compass
- Shay Banon、The future of compass & elasticSearch(CompassとElasticsearchの今後) – https://thedudeabides.com/articles/the_future_of_compass。↩