データ量が増大するにつれ、データベースのクエリ性能が低下したり管理が複雑になったりすることがあります。
そのような課題を解決するために使われる重要な技術がパーティションテーブル(テーブルパーティショニング)です。
テーブルを論理的・物理的に複数の部分(パーティション)に分割することで、クエリのパフォーマンス向上・管理効率の改善・スケーラビリティの確保が実現できます。
本記事では、パーティションテーブルの基本概念から水平分割・垂直分割の違い・パフォーマンス向上のメカニズム・GUIDを使った設計手法まで、データベース設計に必要な知識を詳しく解説します。
データベース設計に関わるエンジニアやDBAの方はぜひ参考にしてみてください。
パーティションテーブルとはテーブルを分割してパフォーマンスを向上させる技術(結論)
それではまず、パーティションテーブルの基本的な意味と目的について解説していきます。
パーティションテーブルとは、1つの大きなテーブルを「パーティション」と呼ばれる複数の小さな部分に論理的または物理的に分割する技術です。
ユーザーからはあくまで1つのテーブルとして見えますが、内部的には複数のパーティションに分かれて格納されています。
パーティションテーブルの主なメリットは3つです。①クエリパフォーマンスの向上(検索対象パーティションの絞り込みによるI/O削減)、②管理性の向上(パーティション単位でのバックアップ・削除・アーカイブが可能)、③可用性の向上(特定パーティションのメンテナンス中も他パーティションは稼働継続)です。
特に数億〜数十億行規模の大規模テーブルに対してパーティショニングを適用することで、劇的なパフォーマンス改善が得られることがあります。
PostgreSQL・MySQL・Oracle・SQL Serverなど主要なRDBMSがパーティショニングをサポートしています。
パーティションプルーニングとは
パーティショニングによるパフォーマンス向上の中核となる仕組みがパーティションプルーニング(Partition Pruning)です。
クエリのWHERE句にパーティションキーが含まれている場合、データベースエンジンは関係のないパーティションを自動的に除外(プルーニング)し、必要なパーティションだけを検索します。
例:売上テーブルを年月でパーティション分割している場合
SELECT * FROM sales WHERE sale_date BETWEEN ‘2024-01-01’ AND ‘2024-03-31’;
→ 2024年1〜3月のパーティションのみスキャン
→ 他の年・月のパーティションは検索対象外
→ テーブル全体の1/Nのデータだけスキャンすれば済む
データが10年分あっても特定の3ヶ月分だけをスキャンできるため、クエリのI/O量が大幅に削減されます。
パーティションテーブルの主な分割方式
| 分割方式 | 概要 | 適した用途 |
|---|---|---|
| レンジパーティション | 値の範囲で分割(日付・数値など) | 時系列データ・ログデータ |
| リストパーティション | 特定の値リストで分割(地域・カテゴリなど) | カテゴリ別データ |
| ハッシュパーティション | ハッシュ値で均等分割 | 均等分散が必要なデータ |
| コンポジットパーティション | 複数の方式を組み合わせ | 複雑な分散要件 |
水平分割と垂直分割の違い
続いては、パーティショニングの2大手法である水平分割と垂直分割の違いについて確認していきます。
テーブルを分割する方向性によって、水平分割と垂直分割の2種類があります。
水平分割(Horizontal Partitioning)
水平分割はテーブルの「行(レコード)」を分割する手法です。
列の構成は変わらず、行のデータが複数のパーティションに振り分けられます。
最も一般的なパーティショニング手法であり、データベースエンジンが直接サポートしている「テーブルパーティショニング」は通常この水平分割を指します。
水平分割の例(日付によるレンジパーティション)
ordersテーブル(全データ)
↓ 水平分割
orders_2022(2022年のデータ)
orders_2023(2023年のデータ)
orders_2024(2024年のデータ)
→ 各パーティションは同じ列構成を持つ
水平分割は時系列データ・ログデータ・トランザクションデータなど、データ量が継続的に増える大規模テーブルに特に有効です。
垂直分割(Vertical Partitioning)
垂直分割はテーブルの「列(カラム)」を分割する手法です。
1つのテーブルを列のグループごとに複数のテーブルに分割します。
主キーは各分割テーブルで共通に保持します。
垂直分割の例
usersテーブル(id, name, email, password, address, profile_image, bio)
↓ 垂直分割
users_basic(id, name, email, password)← 頻繁にアクセスする列
users_profile(id, address, profile_image, bio)← アクセス頻度が低い列
頻繁にアクセスする列とほとんどアクセスしない列を分けることで、I/O効率が向上します。
また大きなBLOBデータ(画像・テキスト)を別テーブルに分離することで、メインテーブルのスキャン効率が改善されます。
シャーディングとパーティショニングの違い
パーティショニングと混同しやすいのがシャーディング(Sharding)です。
パーティショニングは単一のデータベースサーバ内でテーブルを論理的・物理的に分割する手法ですが、シャーディングは複数の独立したデータベースサーバにデータを分散する手法です。
シャーディングはパーティショニングよりも大規模なスケールアウトが必要な場面で使われますが、トランザクション管理やジョインが複雑になるという難点があります。
GUIDを使ったパーティション設計手法
続いては、GUIDをパーティションキーとして使う場合の設計上の注意点と対策について確認していきます。
GUIDパーティションテーブルについては次の記事でも詳しく解説していますが、ここではデータベース設計の観点から説明します。
GUIDをパーティションキーにする問題点
GUID(Globally Unique Identifier)は128ビットの一意識別子で、主キーとして広く使われています。
しかしランダムなGUIDをパーティションキーに使うことは、通常のパーティショニング設計では推奨されません。
その理由として、まずパーティションプルーニングが効かない点があります。
ランダムGUIDはレンジパーティションに向かず、WHERE句でGUIDの範囲を指定することが稀なためです。
次にデータの偏りがありません。
ランダムGUIDはハッシュパーティションでは均等分散しますが、時系列やカテゴリに基づいた局所性がないため管理上のメリットが得にくいです。
GUIDの問題を解消する設計手法
GUIDを主キーとして使いながらパーティショニングのメリットを得るための設計手法として、以下があります。
①順序付きGUID(Sequential GUID)の使用
COMB GUID・ULID(Universally Unique Lexicographically Sortable Identifier)など、
タイムスタンプを先頭に含む時間順序付きのGUIDを使う
→ 時系列でのレンジパーティションが可能になる
②複合主キー設計
created_atなどの日付列を別途持ち、パーティションキーとして使用
GUIDは一意性のためだけに使い、パーティション分割は日付で行う
PostgreSQLでのパーティションテーブル作成例
PostgreSQLでのレンジパーティションテーブルの作成例を示します。
— 親テーブルの作成
CREATE TABLE orders (
order_id UUID DEFAULT gen_random_uuid(),
order_date DATE NOT NULL,
amount DECIMAL(10,2)
) PARTITION BY RANGE (order_date);
— パーティションの作成
CREATE TABLE orders_2023 PARTITION OF orders
FOR VALUES FROM (‘2023-01-01’) TO (‘2024-01-01’);
CREATE TABLE orders_2024 PARTITION OF orders
FOR VALUES FROM (‘2024-01-01’) TO (‘2025-01-01’);
パーティションテーブルの設計ベストプラクティス
続いては、パーティションテーブルを設計する際のベストプラクティスと注意点について確認していきます。
適切なパーティションキーの選択
パーティショニングの効果を最大化するには、パーティションキーの選択が最も重要です。
良いパーティションキーの条件として、まずクエリのWHERE句に頻繁に登場することが挙げられます。
プルーニングが効くためです。
次にデータが均等に分散されることです。
特定のパーティションだけが極端に大きくなる「ホットスポット」を避ける必要があります。
また将来のデータ追加・削除を考慮した設計であることも重要です。
時系列データなら古いパーティションを丸ごとDROP TABLEで削除でき、DELETEより格段に高速です。
パーティション数の設計
パーティション数が多すぎると管理が複雑になり、クエリプランナーへの負荷も増えます。
一般的なガイドラインとして、PostgreSQLでは数百〜数千程度が実用的なパーティション数の目安とされています。
月次パーティションなら1年で12、10年で120となり管理が容易です。
日次パーティションは細かすぎる可能性があるため、データ量に応じて判断する必要があります。
パーティションテーブルのインデックス設計
パーティションテーブルでは、インデックスは各パーティションごとにローカルインデックスとして作成されるのが基本です。
グローバルインデックス(全パーティションを横断するインデックス)はデータベースによってサポートが異なります。
ローカルインデックスはパーティション削除時に自動的に削除されるため管理が容易ですが、パーティションキー以外の列での検索は全パーティションを対象にすることになるため注意が必要です。
まとめ
パーティションテーブルとは大規模テーブルを複数のパーティションに分割することで、クエリのパフォーマンス向上・管理効率の改善・可用性の向上を実現するデータベース技術です。
水平分割(行単位)と垂直分割(列単位)の2種類があり、大規模テーブルには水平分割が最もよく使われます。
パーティションプルーニングにより関係のないパーティションをスキャン対象から除外できるため、検索パフォーマンスが大幅に向上します。
GUIDをパーティションキーにする際はランダム性の問題があるため、順序付きGUIDや複合主キー設計を検討することが重要です。
適切なパーティションキーの選択とパーティション数の設計が、パーティショニングの効果を最大化する鍵となるでしょう。