こんにちは!データベースエンジニアの技術ブログへようこそ。
今日は、PostgreSQLを使っていると必ずと言っていいほど直面する「検索が遅い!」という悩みについて、ちょっと面白い解決策をお話ししますね。
データベースのインデックス(索引)といえば、辞書の「目次」のようなものだと聞いたことはありませんか? まさにその通りなのですが、実はこの目次、「そのままの形」でしか探せないという少し頑固な一面があるんです。
どういうことか、一緒に紐解いていきましょう。
—
辞書から「小文字」を探したいとき、何が起きる?
たとえば、ユーザー登録名がズラリと並んだリストがあるとします。
「田中」「tanaka」「TANAKA」……。これらがバラバラに登録されているデータベースから、ユーザーが検索窓に「tanaka」と入力したとき、全部ヒットさせたいですよね。
そんなとき、私たちはよくこう書きます。
`SELECT FROM users WHERE LOWER(name) = ‘tanaka’;`
「名前を全部小文字(LOWER)に変換してから、’tanaka’ と一致するものを見つけてね!」という命令です。
でも、データベース側は困ってしまいます。なぜなら、データベースが持っているインデックスは「元々の名前(田中、tanaka…)」を基準に作られているからです。「小文字に変換した後の名前」なんて、インデックスには書いていないんですよ。
結果として、データベースは仕方なく最初から最後まで全ての行を目視チェックする「フルスキャン」という重労働を強いられます。これが、検索が遅くなる最大の原因です。
「関数ベースのインデックス」という裏技
ここで登場するのが、今回の主役「関数ベースのインデックス」です。
これは簡単に言うと、「計算済みの結果を、あらかじめ索引帳に書き込んでおく」という手法です。
料理の仕込みに例えると…
イメージしてみてください。あなたはレストランのオーナーです。注文が入るたびに野菜を洗って切っていたら、お客様をお待たせしてしまいますよね?
そこで、あらかじめ「カット済み野菜」のストックを用意しておくことにしました。これなら、注文が入った瞬間にパッと出せますよね。
関数ベースのインデックスは、まさにこれです。
「小文字に変換した状態」をあらかじめ計算しておいて、それを索引帳の別枠として作っておくんです。そうすれば、`LOWER(name)` という検索条件が来たとき、データベースは「あ、これなら計算済みリストに書いてあるぞ!」と即座に答えを見つけ出せるようになります。
どうやって書くの?
PostgreSQLでこれを実装するのは驚くほど簡単です。普段のインデックス作成コマンドに、ちょっと計算式を混ぜるだけ。
CREATE INDEX idx_users_lower_name ON users (LOWER(name));
たったこれだけ!
これだけで、データベースは `LOWER(name) = ‘…’` という検索条件を見るたびに、この専用の索引帳を使って爆速で検索してくれるようになります。
注意点も、少しだけ
とても便利な機能ですが、一つだけ覚えておいてほしいことがあります。それは「何でもかんでもインデックスを作らないこと」です。
- 書き込みが少し遅くなる: データが増えるたびに「計算後の値」も保存し直さないといけないので、データの更新頻度が高いテーブルでは少し負荷になります。
- ストレージ容量を使う: 索引帳が分厚くなるので、ディスク容量を少し多めに消費します。
「よく検索するけど、更新はそこまで頻繁じゃない」という場所にこそ、このテクニックは輝きます。
—
最後に
データベース設計は、まるで図書館の整理整頓のようなものです。「どういう風に検索されるかな?」と想像しながら、適切な索引を用意してあげる。そうすると、データベースは期待に応えて、驚くほど軽快に動いてくれるようになります。
もし今、あなたのサービスで「検索が重いな」と感じている箇所があったら、ぜひこの「関数ベースのインデックス」を思い出してみてください。
皆さんのデータベースが、今日も快適に動きますように!それでは、また次回の記事でお会いしましょう。
コメント