こんにちは、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();