CodeGym /コース /SQL SELF /大量データ向け分析関数の最適化:インデックスとパーティショニング

大量データ向け分析関数の最適化:インデックスとパーティショニング

SQL SELF
レベル 60 , レッスン 3
使用可能

データがめっちゃ増えてくると(会社のチャットで締め切りメッセージが溢れるみたいに)、SELECTや集計クエリが遅くなってくるんだ。主な理由はこれ:

  1. インデックスがない。PostgreSQLがクエリ実行のためにテーブル全体をスキャンしなきゃいけない時(これを"Seq Scan"=シーケンシャルスキャンって言う)、クエリがかなり遅くなるよ。
  2. 非効率なSQLクエリ。クエリが最適化されてないと、インデックスがあってもパフォーマンス問題にぶつかる。例えばWHEREでキー条件を忘れたら?実行時間がめっちゃ長くなるよ。
  3. 1つのテーブルに大量データ。例えば全年度の売上を一気に分析しようとすると、インデックスだけじゃ救えないこともある。

でも心配しなくてOK!この問題には2つの定番テクがある:インデックスとパーティショニング。

クエリ高速化のためのインデックス利用

インデックス作成のシンプルな例を見てみよう:

CREATE INDEX idx_sales_date ON sales(transaction_date);
  • ここでidx_sales_dateはインデックス名(好きな名前でOKだけど、意味が分かる名前がベター)。
  • ON sales(transaction_date)は、どのテーブルのどのカラムにインデックスを作るか指定してる。

このインデックスは、transaction_dateでよくフィルタする時に特に便利!

このインデックスで速くなるクエリ例:

SELECT *
FROM sales
WHERE transaction_date BETWEEN '2023-01-01' AND '2023-12-31';

複合キーのインデックス

もしクエリで複数カラム(例えばregionproduct_id)をよく使うなら、複合インデックスを作るのもアリ:

CREATE INDEX idx_sales_region_product ON sales(region, product_id);

こういうクエリがめっちゃ速くなるよ:

SELECT *
FROM sales
WHERE region = 'North America' AND product_id = 42;

ユニークインデックスの利用

ユニークインデックスは検索を速くするだけじゃなく、カラムの値のユニーク性も保証してくれる。例えば:

CREATE UNIQUE INDEX idx_unique_customer_email ON customers(email);

これで同じメールアドレスの顧客をうっかり2人登録しちゃうことがなくなる。

分析関数向けのインデックス

SUMCOUNTAVGみたいな分析関数も、インデックスを使うことで集計が速くなることがある。例:

CREATE INDEX idx_sales_amount ON sales(amount);

クエリ:

SELECT SUM(amount)
FROM sales 
WHERE transaction_date >= '2023-01-01';

インデックスのおかげでこのクエリも速くなる。

大量データ対応のためのテーブルパーティショニング

テーブルパーティショニングは、大きなテーブルを論理的に小さいパーツ(パーティション)に分けること。例えばsalesテーブルを年度ごとに分けて、sales_2021sales_2022みたいにできる。

難しそう?実はPostgreSQLなら意外とカンタン!

パーティショニングの種類

  1. レンジパーティショニングRange Partitioning)。例えば日付で範囲ごとに分ける。
  2. リストパーティショニングList Partitioning)。例えば地域ごとに値で分ける。
  3. ハッシュパーティショニングHash Partitioning)。ハッシュ関数で分ける(手動で使うことは少なめ)。

パーティションテーブルの作成

じゃあ、年度ごとにパーティションする売上テーブルを作ってみよう。

CREATE TABLE sales (
    id SERIAL PRIMARY KEY,
    transaction_date DATE NOT NULL,
    amount NUMERIC,
    region TEXT
) PARTITION BY RANGE (transaction_date);

次に、各年度用のパーティションを作成:

CREATE TABLE sales_2021 PARTITION OF sales
FOR VALUES FROM ('2021-01-01') TO ('2022-01-01');

CREATE TABLE sales_2022 PARTITION OF sales
FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');

日付でフィルタするクエリは自動的に必要なパーティションだけ使う。EXPLAINコマンドで確認できるよ。

パーティショニングの例

2021年だけの売上合計を出すクエリはこんな感じ:

SELECT SUM(amount)
FROM sales
WHERE transaction_date BETWEEN '2021-01-01' AND '2021-12-31';

見ての通り、PostgreSQLはsales_2021パーティションだけ見て、全テーブルをスキャンしない。

例:地域ごとのメトリクス集計の最適化

例えば、地域ごとの売上合計を計算したいとする。インデックスもパーティションもなければ超遅い。まずregionカラムにインデックスを作ろう:

CREATE INDEX idx_sales_region ON sales(region);

クエリ:

SELECT region, SUM(amount)
FROM sales
GROUP BY region;

インデックスのおかげで処理が速くなる!

例:時系列データのパーティショニング

トランザクションやログみたいな時系列データは、月ごとにパーティションを作るのがオススメ。例えば:

CREATE TABLE sales_monthly PARTITION BY RANGE (transaction_date);

CREATE TABLE sales_jan_2023 PARTITION OF sales_monthly
FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');

クエリ:

SELECT SUM(amount)
FROM sales_monthly
WHERE transaction_date >= '2023-01-01' AND transaction_date < '2023-02-01';

PostgreSQLはsales_jan_2023パーティションだけ読むから、速い!

例:インデックスとパーティショニングの組み合わせ

インデックスとパーティショニングは組み合わせて使える。例えば各パーティション内にインデックスを作ることもできる。例:

CREATE INDEX idx_sales_amount_jan_2023 ON sales_jan_2023(amount);

よくあるミスを避けるには

パフォーマンス問題の多くは、インデックスやパーティショニングの使い方ミスが原因。例えば:

  • インデックスが多すぎるとINSERTが遅くなる。
  • パーティションは均等にデータが入るように設計しよう。小さすぎても大きすぎてもパフォーマンスが落ちる。
  • 最適化前にパフォーマンス分析(EXPLAIN ANALYZE)を忘れるのは、車を修理するのにボンネットを開けないのと同じ。

最適化で本当に速くなってるか、ちゃんとチェックしよう!色々試してみるのも大事だよ。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION