PostgreSQLが遅い原因を解決!初心者向けデータベースチューニングと高速化の基本
生徒
「PostgreSQLを使っているのですが、最近データの検索にすごく時間がかかるようになったんです。パソコンの故障でしょうか?」
先生
「それはパソコンの故障ではなく、データベースの『整理整頓』が追いついていないのかもしれません。PostgreSQL(ポストグレスキューエル)は世界中で使われている優れたデータベースですが、使い続けるうちに『動きが重くなる原因』が溜まっていくんです。」
生徒
「整理整頓ですか?なんだかお部屋の掃除みたいですね。プログラミングが全くわからない私でも、速くする方法を理解できますか?」
先生
「もちろんです。例えば、巨大な図書館で1冊の本を探すシーンを想像してみてください。本がバラバラに置かれていたら大変ですよね?データベースもそれと同じです。今回は、なぜ遅くなるのか、どうすれば速くなるのかを、初心者の方にも分かりやすく解説しますね。」
1. なぜPostgreSQLの処理が遅くなるのか?
PostgreSQL(ポストグレスキューエル)は、たくさんの情報を「テーブル」という表形式で保存するソフトウェアです。 最初はサクサク動いていても、データが増えるにつれて「検索」や「保存」の処理が重くなることがあります。
この「遅くなる」という現象には、いくつかの大きな原因があります。 もっとも多いのは、「インデックス(索引)」が正しく使われていないこと、そして「不要なデータのゴミ」が溜まっていることです。 まずは、データがどのように保存されているか、簡単な名簿の例で見てみましょう。
2. インデックスがないと「全件検索」で時間がかかる
データベースが遅くなる最大の原因の一つが、「シーケンシャルスキャン」と呼ばれる動作です。 これは、辞書を1ページ目から最後のページまで全部めくって言葉を探すような状態です。データが10件なら一瞬ですが、100万件あったら日が暮れてしまいますよね。
そこで役立つのが「インデックス」です。これは本の「索引」のようなもので、あらかじめ「どこに何があるか」をメモした別紙を用意しておくことで、目的のデータをすぐに見つけられるようにする仕組みです。
例えば、次のような「顧客名簿(customers)」というテーブルがあるとします。
id | name | city | points
---+-----------+-----------+--------
1 | 田中太郎 | 東京 | 500
2 | 佐藤花子 | 大阪 | 1200
3 | 鈴木一郎 | 名古屋 | 300
4 | 高橋愛 | 福岡 | 850
5 | 伊藤健 | 東京 | 150
6 | 渡辺裕子 | 札幌 | 2000
ここから「東京に住んでいる人」を探す命令(SQL)を書いてみます。
SELECT *
FROM customers
WHERE city = '東京';
実行結果は以下のようになります。
id | name | city | points
---+-----------+-----------+--------
1 | 田中太郎 | 東京 | 500
5 | 伊藤健 | 東京 | 150
もし「city」という項目にインデックスが貼られていないと、PostgreSQLは「1番目は東京かな?違う。2番目は?違う…」と1行ずつ確認します。 これが「遅い」の原因です。インデックスを設定することで、この作業をショートカットできます。
3. 古くなったゴミ(不要な領域)が溜まっている
PostgreSQLには、他のデータベースとは少し違う「データの更新ルール」があります。 データを「書き換える(UPDATE)」や「消す(DELETE)」をしたとき、実は元のデータはすぐには消えません。 「古いデータに『もう使わないよ』という印をつけるだけ」で、中身は残ったままなのです。
これを放置すると、テーブルの中に透明なゴミがどんどん増えていき、データベースがどんどん肥大化します。 この現象を「肥大化(ブラウト)」と呼びます。 これを解決するために、PostgreSQLには「VACUUM(バキューム)」というお掃除機能が備わっています。
「掃除機をかける」という意味の通り、不要になった領域を回収して、新しいデータが再利用できるようにしてくれます。 通常は自動で行われますが、大量のデータを一度に消した後は、手動で掃除が必要な場合もあります。
4. 複雑な計算を何度もさせている
データベースは計算も得意ですが、あまりにも複雑な条件を指定すると疲れてしまいます。 例えば、「ポイントが1000以上の人を、ポイントが高い順に並べて表示する」という操作を考えてみましょう。
SELECT name, points
FROM customers
WHERE points >= 1000
ORDER BY points DESC;
実行結果は以下のようになります。
name | points
----------+--------
渡辺裕子 | 2000
佐藤花子 | 1200
このように「並べ替え(ORDER BY)」をするとき、メモリという「作業机」が狭いと、PostgreSQLは一度ハードディスクという「倉庫」にデータを置いて作業をします。 机の上で作業するより、いちいち倉庫まで往復する方が時間がかかりますよね。 これが、チューニングで「メモリ設定(work_mem)」を調整する理由です。
5. 統計情報が古くなっている
PostgreSQLは、命令を受けると「どうすれば一番速くデータを取ってこれるか」という作戦を立てます。 この作戦会議を「クエリ実行計画(プランニング)」と呼びます。
この作戦を立てるために、PostgreSQLは「このテーブルには大体何行くらいのデータがあって、どんな値が多いのか」という「統計情報」を持っています。 しかし、短時間に大量のデータを追加したり削除したりすると、この情報の更新が追いつかず、PostgreSQLが「今はデータが少ないはずだ」と勘違いして、間違った作戦(遅いルート)を選んでしまうことがあります。
これを防ぐために、最新の状態を教えてあげる「ANALYZE(アナライズ)」というコマンドがあります。
6. データを追加・変更する時の速度低下
検索だけでなく、「保存(INSERT)」や「更新」が遅くなることもあります。 意外な原因として、先ほど紹介した「インデックス」の作りすぎが挙げられます。
索引があれば探すのは速くなりますが、新しいデータを追加するたびに、すべての索引も書き換えなければなりません。 教科書に何百個も付箋を貼っていたら、新しく1ページ追加したときに付箋を貼り直すのが大変ですよね。 パフォーマンスを上げるためには、本当に必要な項目にだけインデックスを貼る、というバランス感覚が大切です。
例えば、新しく「高橋愛」さんの情報を追加するSQLは以下のようになります。
INSERT INTO customers (id, name, city, points)
VALUES (7, '加藤純', '横浜', 600);
追加後のテーブルはこのようになります。
id | name | city | points
---+-----------+-----------+--------
1 | 田中太郎 | 東京 | 500
2 | 佐藤花子 | 大阪 | 1200
3 | 鈴木一郎 | 名古屋 | 300
4 | 高橋愛 | 福岡 | 850
5 | 伊藤健 | 東京 | 150
6 | 渡辺裕子 | 札幌 | 2000
7 | 加藤純 | 横浜 | 600
このように1件追加するだけでも、裏側では「city」の索引や「points」の索引を更新する作業が走っています。 データベースのチューニングとは、こうした「検索の速さ」と「更新の速さ」のちょうどいいバランスを見つける作業なのです。
7. パフォーマンスを改善するための第一歩
「なんだか遅いな」と感じたら、まずは「どの命令が遅いのか」を特定することから始めます。 PostgreSQLには、人間が書いたSQLがどのように処理されるのかを教えてくれる機能があります。
難しい設定をいじる前に、まずは不必要なデータが溜まっていないかを確認し、適切な場所にインデックスを貼る。 そして、定期的にバキューム(お掃除)が行われているかをチェックする。 これだけで、多くのトラブルは解決に向かいます。
プログラミング未経験の方でも、「データベースは巨大な情報の倉庫であり、それを整理整頓し、効率的な探し方のメモ(索引)を作ることが高速化の鍵である」ということさえ覚えておけば、エンジニアとの会話もスムーズになるはずです。