200万レコードの書誌データ検索をMySQLのみで実装した
先日、オンラインコミュニティプラットフォームであるOSIROで「ブックログ機能」をリリースしました。
現在180万冊以上の書籍データを有しており、ユーザーさんはキーワード検索を行って本の読書ログを登録できます。 キーワード検索については、検索エンジンを導入しないでMySQLのみで実装されています。
パフォーマンスも精度も実用上問題なく、結果として検索基盤の運用コストを増やさずに済んでいます。 この記事では
- なぜMySQLで成立すると判断し Elasticsearch を選ばなかったのか?
- 実際にリリースして見えた課題はなにか?
について書いてゆきます。
なぜ Elasticsearch を選ばなかったのか
メンテナンスコストを増やしたくなかったというシンプルな理由です。
Elasticsearch 自体が悪いわけではありません。 Rails + MySQL で完結しているプロダクトに Elasticsearch を追加すると、以下のような複雑さが確実に増えます。
- クラスタの管理
- RDBMSとは異なるスキーマの管理
- DBと検索インデックスの同期どうするか?
AWSの場合Amazon OpenSearch Serviceなどのマネージドサービスを使うにしてもやはりクラスタのことを考えなければ行けないなどコストがゼロになることはありません。
本を登録するにあたり、キーワード検索は重要な機能ですが、検索基盤そのものはプロダクトの差別化ポイントにはなりません。 小さなチームにとって、Elasticsearchの存在が運用コストを高めると考えています。 検索精度を少し上げるためだけに、メンテナンスコストを恒常的に増やす選択はしたくなかったので、Elasticsearchをはじめとした検索エンジンを選びませんでした。
書誌データは最大250万レコードだと分かっていた
日本に流通している書誌データ(ISBNベース)は最大でも約250万レコード程度であると言われています。1年で様々な書籍が出版されますが、それも年間で11万冊程度と言われていて、書籍は増えるが、無限には増えないというような状態で、データ量の上限が事前に見えていました。
初期リリースの要件も「本のタイトルの部分一致検索が行えればまずはよい」ということでしたので、 MySQLでの「FULLTEXTを利用したNgramでの検索」で十分成立するという仮説を最初から持てました。
リリースして分かった本当の課題
実際にMySQLでキーワード検索をスピーディーに実装しました。 その中で社内テストや先行リリースを行って色々なことがわかりました。 パフォーマンスはリリース当初から良好で、200万レコード規模でも検索レスポンスは十分高速で、当初の目論見通りでした。 しかし、検索としてちょっと使いづらい、思ったように書籍がヒットしないということが少し起こっていました。
書誌データの正規化
実際に問題になったのは、次のような点です。
- 『Re:ゼロから始める異世界生活』
ユーザーさんがこの本を探すときに、必ずしも正式な書名を正確に入力するわけではありません。
- 「リゼロ」
といった具合に、記号を省略したり、全角・半角を意識せずに入力するのが自然な行動です。
しかし、書誌データ側には記号データでの差異が存在します。
これらが混在した状態のまま検索を行うと「書名は確かに存在するのにヒットしない」 「一部のキーワードだけでは検索結果が空になる」という「意図しない検索結果」が散見されました。
上記の課題はElasticsearchでも起こる課題である
事前に検索用のデータを整える必要があったのです。 検索の難しさは「エンジン選定」ではなく「検索用のデータをどう作るか」という部分にありました。
最終的には、検索エンジンを用意しないで下記のような仕組みで対応しました。
- 検索専用の関連カラムを用意
- 事前に正規化した文字列を格納
- MySQL FULLTEXTでNgramでの検索を行う
サンプルイメージ
ALTER TABLE books
ADD FULLTEXT INDEX idx_search_text (search_text)
WITH PARSER ngram;
def normalize(text)
text
.tr("0-9A-Za-z", "0-9A-Za-z")
.gsub(/[^\p{Hiragana}\p{Katakana}\p{Han}a-zA-Z0-9]/, "")
.downcase
end
タイトル・著者名・よみを正規化して結合し、それを search_text に保存するという、至ってシンプルなものです。 この検索用のインデックスデータを追加することで、検索速度をあまり損なわず、精度も実用レベルでMySQLで十分な書籍のキーワード検索を作ることができました。
まとめ
結果としては200万レコード規模の書誌検索なら MySQL で必要十分でした。 データ量の現実的な見積もりと早期リリースしたあとの改善策を早期に考えられたことが、快適な検索体験に繋がったと考えています。
要件をシンプルにすれば検索エンジンは不要です。 小さなチームでは複雑さがコストになるので、MySQLのみで書誌データの検索を完結できて本当に良かったと感じています。
皆様も、要件がシンプルで件数も数百万なら、検索エンジンを盲目的に導入するのではなく、まずRDBMSで検索機能の実装を始めてみてはいかがでしょうか?
