こんにちは、Nakaです。
Laravelで業務システムを構築していると、データ量の増加に伴いテーブルのパーティション化を検討する場面が出てきます。検索性能やアーカイブ運用の観点では有効な手法ですが、特に Laravel の Eloquent のように「主キーを前提に更新クエリを組み立てる ORM」を使っている場合は注意が必要です。
Eloquentの save() メソッドによる更新処理は、そのまま使用するとパーティションキーを持つテーブルではパフォーマンス面で問題が生じることがあります。この記事では、こうしたテーブルを Eloquent で更新する際に気をつけるべきポイントと、その対処方法について整理します。なお、本記事では MySQL を前提として説明します。
簡単な例として、注文に紐づく配送情報を管理するシステムを考えます。このシステムでは、日常的に扱うのは「これから配送される予定のデータ」であり、過去の配送データは通常の業務フローでは参照しません(必要な場合のみログや調査用途で参照)。
1件の配送には「配送予定日」「配送ステータス」「配送先住所」などの情報があり、配送予定日を基準にデータを管理します。まず配送テーブルの定義を提示します。
CREATE TABLE deliveries (
id BIGINT AUTO_INCREMENT,
order_id BIGINT NOT NULL,
scheduled_at DATETIME NOT NULL, -- パーティションキー
status VARCHAR(20) NOT NULL,
address VARCHAR(500),
created_at DATETIME,
updated_at DATETIME,
PRIMARY KEY (id, scheduled_at),
INDEX idx_order (order_id, scheduled_at)
) PARTITION BY RANGE COLUMNS(scheduled_at) (
PARTITION p202601 VALUES LESS THAN ('2026-02-01 00:00:00'),
PARTITION p202602 VALUES LESS THAN ('2026-03-01 00:00:00'),
PARTITION p202603 VALUES LESS THAN ('2026-04-01 00:00:00'),
PARTITION pmax VALUES LESS THAN (MAXVALUE)
);
status には配送の進行状況を表す文字列を保持します。本記事では以下を想定します。
scheduled_at をパーティションキーとして月ごとに分割し、将来日付のデータを安全に受け入れるため MAXVALUE パーティションも定義しています。検索時には常に「現在時刻より未来の配送予定日」に絞り込む設計です。つまりアプリケーションコードでは scheduled_at >= now() の条件が必ず含まれる想定です。
このように、
といった要件を満たす場合、日付ベースのパーティションは非常に相性が良い設計になります。MySQL は WHERE 句に含まれるパーティションキーをもとに対象パーティションを限定できるため、不要なデータを物理的にスキャンせずに済み、検索・更新のパフォーマンスを安定させることができます。
Eloquent モデルでは scheduled_at がパーティションキーであるため、save()メソッドを用いて更新した場合に注意が必要です。例えば以下のコードを実行したとします。
$delivery = Delivery::where('id', 1)
->where('scheduled_at', '>=', now())
->first();
$delivery->status = 'in_transit';
$delivery->save();