DBのインデックスと戦うのやめて Elasticsearch に丸投げしたらいいことあった話

こんにちわ、ワカルのCTOの包です。
今日で2015 WACUL Advent Calendar 2015 最後の日です。

この１年は、会社としても大きく成長した一年でした。すべて埋まった(!)アドベントカレンダーの記事を振り返ってみると、技術者の層がとても厚くなったなと感じます。

なにより、忙しい業務の合間に楽しい記事を書いてくれた会社のみんな、忙しい中寄稿してくれた @kenzan100 さん、技術メンバーの働き方に理解を示してくれて共に頑張っている会社のみんなに感謝しています。

## データの検索は Elasticsearch に丸投げしたらいいことあった話

さて、本題です。
今年ワカルで導入した新しい道具の中でも特に導入して良かったな、と思うのものの１つは Elasticsearch です。

この記事では、 Elasticsearch を導入した経験を元に、開発全体のコスト面や運用面などから見た時のおすすめする点について小話をまとめました。
設定とかクエリの投げ方とかの細かい技術的な話はあまりでてこないです。

## 対象読者

この記事の対象読者は、以下のような項目にあてはまる人です。

* アプリのバックエンドの設計・実装に関わっていて、まだ elasticsearch は導入していない
* 扱うデータの件数が多い (100万件以上とか)
* 全文検索は別に使ってない(もしくはDBでがんばってる)
* 複雑な絞り込み条件でパフォーマンスが気になっている
    * DBのインデックスで消耗してる

## Elasticsearch とは

Elasticsearch は、Elasticsearch 社が提供する、RestAPIや、複数サーバーでのクラスタリングなどの機能を持った検索システムです。基本的な機能は無料で使えます。プラグインや、連携するシステムの一部が有料で提供されています。
内部では、 Apache Lucene のインデックスが使われています。Lucene 自体は相当古く枯れたソフトウェアという印象があります。
Luceneを使った検索システムとしては、他に、 Apache Solr などがありますが、近年では一番勢力を伸ばしているソフトウェアです。
[kibana](https://www.elastic.co/products/kibana) でデータを可視化するのも人気ですし、AWSは最近、 [Elasticsearch Service](https://aws.amazon.com/jp/elasticsearch-service/) というマネージドなサービスを始めたりしています。

## 増えるデータと、検索に関わるコスト増加にどう対応したか

サービスを運用しているなかで、情報の検索に関わる実装コストは、データが多くなるにつれて増えていきます。
DBのインデックス管理や、複雑なクエリのチューニングなど、とても多くのことを気にしなくてはいけません。
一方、サービスの成長には、いろいろな切り口でのデータの見せ方に素早く対応しなければいけませんし、パフォーマンスも重要です。

ワカルのプロダクトである AIアナリストは MongoDBのみを使っていました。リリース３ヶ月ぐらいで、データが増えてきた時、上記のような悩みを抱え始めていました。
AIアナリストでは、GoogleAnalyticsなどから集めた情報を分析して、様々ま切り口でユーザーに可視化する機能があるのですが、これはいわばクローリングのようなことをしてデータを集めているため、１ユーザーあたりのデータ量がとても大きいです。
ユーザー数が増え、データ件数が、100万を超えたあたりで、既存の MongoDB でリアルタイムに検索する際に、パフォーマンス不足になり、インデックスの組み合わせの管理が煩雑になってきました。

特に、新しい要件を追加したくなり、あるフィールドAで絞り込んで、別の値がある範囲にあって、結果をまた別のフィールドCで並び替える。みたいなことをする場合には、最適なインデックスについていちいち(それも組み合わせで！) 考えなければならず、開発の工数として重くなりそうな気配がありました。

そんなとき、弊社に入社してくれた @kanagi がたまたまElasticsearchの検証をやっていて、全文検索だけじゃなくて、更新頻度の低いタイプのデータだったら、向いてそうという話は聞いていたため、試しに組み込んで見ることにしました。

## Elasticseachを検索用にどう組み込んだか

方針として、以下の様にしました。

* マスタデータは、MongoDBに
* MongoDB側は最低限のインデックス(id, 日付など) のみ
* マスタデータをElasticsearch にインポートするコマンドを作成しておく
* アプリからのデータ書き込みは、マスタDB,Elasticsearch  両方に書き込まれるようにする
* 関連データのひも付けが必要な場合には、elasticsearchで検索後に適宜MongoDBから紐付ける

Elasticseach を利用するときは、マスタデータは別の場所に置いておき (DBや、S3など) 検索用のインデックスを常にそこから構築できるようにしておくのが吉です。
データのバックアップという意味もありますが、Elasticseach では、基本的にテーブル定義(mapping と呼ばれる)の構成を変えられないため、新しいmappingに切り替えるには、新しく全データを流し込む必要があるためです。

クラスタは、AWS上で3台の m3.medium インスタンスで構築しました。

## 導入後の改善効果と開発、運用コストについて

### 複雑な組み合わせ条件での検索も高速に

導入当初から、性能が安定していて、現時点では、1000万件以上のデータがあるインデックスに複数の絞込、ソート条件の組み合わせで使っていますが、概ね 50ms 以内の検索が可能になっています。

### アプリ開発のコスト

なにより、複雑な検索要件に対して、elasticsearchに丸投げすれば高速で処理できるため安心して開発できるようになりました。DBのスロークエリの調査、インデックスの構築などはそれなりに高度な知識と、計測作業が必要になるため、その分を削減できた点が大きいです。
当然Elasticsearchにデータを投入するタイミングや管理などについては、追加実装が必要になりますが、一度仕組み化してしまえば、大したことはないです。

### インフラ運用のコスト

管理するソフト・サーバーが増えるため、運用のコストは増えます。
ただ、Elasticseach のクラスタは、かなり安定して、自動で各サーバーへのレプリケーションや、処理の分散を自動で行ってくれます。
追加のサーバーの参加や、アップデートも１台ずつクラスタから外して行うなど柔軟にできます。
予期せぬ事態に今のところは出くわさず、安定して稼働しているため、一度導入してしまえば、運用コストは低い方だと思います。

## まとめ

データベースの検索パフォーマンス管理は、とても頭を使う作業ですし、一般的なWebサービスにおいては、ユーザーの体感するパフォーマンスにもろに影響を与える部分です。
新しいインフラを導入するのは、なかなか重い判断ですが、Elasticsearch は、仕組みを理解して使えば運用も難しくないですし、全体の開発運用コストをみても良い投資だったなと思います。
サービスの検索の機能を、elasticsearch に丸投げするだけで、サービスが成長していく相当な期間、悩みを減らすことができるので、一度検討してみる価値があると思います。