MYSQLのパーティショニングについて
MYSQLのパーティショニングについて
パーティショニングとは
大規模なデータ(ログデータ、ユーザーデータなど)を取得するときに、効率的に管理して、高速に取得する仕組み。
テーブルをパーティション化することで指定したカラムを基準にテーブルを分割して保存し、クエリ検索時に関連するパーティションだけ取得すれば良くなります。
パーティションは実際には物理的な複数のテーブルを作るわけではなく、データを内部的に複数に分割する仕組みとなっており、ユーザから見たときに1つのテーブルとして見えるが、内部的には複数の条件でデータが分割され保存されています。
パーティション化前後のテーブル比較
パーティション前(元のテーブル)
パーティション後
内部的には条件によって分けて保存されてます。
パーティショニングの種類
パーティショニングには複数の種類があります。今回は以下3つのパーティショニングについて説明します。
- RANGEパーティショニング
- LISTパーティショニング
- HASHパーティショニング
RANGEパーティショニング
特定の範囲(例:期間・数値)を基準にデータを分割する方法。
例えば、ログデータを年度ごとに管理したい場合、created_at(作成日時)カラムを条件にパーティションを分割することで、特定の年度に該当するデータのみを効率的に取得することができます。
基本書式
PARTITION BY RANGE (<条件>) (
PARTITION <名前> VALUES LESS THAN (<値>)
)
例:ログデータを年度ごとのパーティションに分割する場合
CREATE TABLE logs
id INT NOT NULL,
created_at DATE NOT NULL
log_message TEXT,
PRIMARY KEY (id, created_at
)
PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2022 VALUES LESS THAN (2023),--2022のデータ
PARTITION p2023 VALUES LESS THAN (2024), --2023のデータ
PARTITION p_max VALUES LESS THAN MAXVALUE,
)
※MAXVALUEは、MySQLのRANGEパーティションで使用される特殊な値で、任意の値を受け入れることが可能です。例えば、2024年のデータが必要になった場合、この設定ではp_maxに対応します。ただし、年は毎年増え続けるものなので、将来的に新しい年度を追加する際には、ALTER TABLE
ステートメントを使用してパーティションを追加する必要があります。
RANGEパーティショニングが役立つケース:
- 古いデータを効率的に削除したいとき。
- 日付や時間に基づく検索が多いとき。
- パーティションされたカラムを利用したクエリを頻繁に実行するとき。
LISTパーティショニング
特定のカラムの値を基準にデータを分割するパーティション方式。
RANGEパーティショニングが「値の範囲」でデータを分割するのに対して、LISTパーティショニングは特定の値に基づいてデータを分割します。
値があらかじめ決まっているカテゴリやグループでデータを分ける場合に適している。
基本書式
PARTITION BY LIST (<条件>) (
PARTITION <名前> VALUES IN (<値1>, <値2>, ...)
);
例:地域ごとにパーティションで分割する場合
CREATE TABLE customers (
id INT NOT NULL,
region VARCHAR(10),
name VARCHAR(50),
PRIMARY KEY (id, region)
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('North'),
PARTITION p_south VALUES IN ('South'),
PARTITION p_west VALUES IN ('West'),
PARTITION p_east VALUES IN ('East')
)
※1:MAXVALUE
などの「すべての状況に対応する」ものは。パーティション式で予期されるすべての値を PARTITION ... VALUES IN (...)
句で指定する必要があります。
※2:MySQL 8.0 では、LIST COLUMNS
パーティション化もサポートされています。整数型以外のカラムを使用したり、複数のカラムをパーティション化のキーにすることができます。
LISTパーティショニングが役立つケース:
- 特定のカテゴリやグループに基づいてデータを分割したいとき
- 固定値に基づいた検索が多いとき
- 範囲ではなくリストで分割したいとき
HASHパーティショニング
どのデータがどのパーティションに入るかを決めるために、指定したカラムの値や式をハッシュ関数で処理します。その結果からデータを指定したパーティションに均等に割り当てます。
基本書式
PARTITION BY HASH (<条件>)
PARTITIONS <パーティション数>;
例:ユーザーIDを基準に均等に分割
CREATE TABLE user_logs (
id INT NOT NULL,
user_id INT NOT NULL,
log TEXT,
PRIMARY KEY (id, user_id)
)
PARTITION BY HASH(user_id)
PARTITIONS 4;
HASHパーティションが役立つケース:
- ランダムアクセスが多い場合
SELECT * FROM session_logs WHERE user_id = 12345;
このクエリだと該当するパーティションを1つだけスキャンすればいいので効率が良くなります。
感想
パーティション機能は用途によって非常に魅力的に見える一方で、実際には扱いが難しい側面もあると感じました。
例えば、LISTパーティショニングやRANGEパーティショニングでは、項目や条件が増えるたびに手動でパーティションを追加する必要があります。これにより、大規模なシステム(例: 年度やカテゴリが頻繁に追加されるシステム)では、管理コストが大幅に増加する可能性があります。このような運用負担を考慮しないと、後々のメンテナンスが非常に手間になる懸念があります。
また、Qiitaの記事にもあるように、パーティションキーに含まれない条件で絞り込むクエリでは、パーティションプルーニングが効かず、全パーティションをスキャン(フルスキャン)してしまう場合があります。これでは、パフォーマンス向上のために導入したパーティションが、かえってパフォーマンスを悪化させる結果になりかねません。
個人的に使うケースは結構限られそうかなと思うので、インデックス追加やクエリ効率化、テーブル設計の最適化を先に行うのがいいのかなと思います。
参考資料
Discussion