软删除字段 deleted_at 必须作为分区键一部分,否则查询无法分区裁剪;应使用 range columns(deleted_at) 或复合分区,并为 deleted_at 单独建索引,清理分区前需按 deleted_at 时间条件筛选。

软删除字段 deleted_at 必须放在分区键里
MySQL / PostgreSQL 按时间分区时,所有 WHERE 条件中用于过滤分区的列(即分区键),必须包含在查询谓词里,否则可能全分区扫描。Laravel 软删除默认靠 deleted_at IS NULL 过滤,如果分区键是 created_at 或 updated_at,但 deleted_at 不在分区定义中,查询就会失效——哪怕加了 ->withTrashed() 也一样。
实操建议:
- 用
RANGE COLUMNS(deleted_at)或RANGE COLUMNS(created_at, deleted_at)分区,确保deleted_at是分区键一部分 - 避免用
LIST或HASH分区,它们不支持NULL值分区,而deleted_at初始为NULL - PostgreSQL 需用
PARTITION BY RANGE (deleted_at),且每个子分区要显式包含NULL区间,例如:FOR VALUES FROM (MINVALUE) TO ('2024-01-01')
SoftDeletes trait 与 whereRaw 查询冲突
分区表 + 软删除下,手动写 whereRaw('deleted_at IS ?') 很容易漏掉 NULL 和具体时间值的双重逻辑,导致查不到已软删记录,或误查出未软删数据。
常见错误现象:调用 Model::withTrashed()->whereRaw('created_at > ?', now()->subMonth())->get() 返回空,实际有数据——因为 MySQL 优化器没走分区裁剪,又没命中 deleted_at 索引。
实操建议:
- 永远优先用
->withTrashed()/->onlyTrashed(),它们会自动注入deleted_at的完整判断逻辑 - 若必须用
whereRaw,补全三态判断:whereRaw('(deleted_at IS NULL OR deleted_at >= ?)', [$time]) - 给
deleted_at加单独索引(即使它是分区键),否则onlyTrashed()查询可能变慢
迁移中创建分区表时绕不开 DB::unprepared()
Laravel 迁移不原生支持 PARTITION BY 语法,直接写 Schema::create() 会报错或静默忽略分区定义。
使用场景:需要按月自动归档订单、日志等历史数据,同时保留软删除能力。
实操建议:
- 用
DB::unprepared()执行原生CREATE TABLE ... PARTITION BY RANGE COLUMNS(...) - 在迁移中先建表,再用
DB::unprepared("ALTER TABLE xxx DROP PRIMARY KEY, ADD id BIGINT UNSIGNED AUTO_INCREMENT FIRST, ADD PRIMARY KEY(id, deleted_at)")补主键(MySQL 要求分区键必须含在主键/唯一键中) - 别在
up()里直接执行php artisan migrate:fresh,分区语句在某些 MySQL 版本下不支持事务,失败后难回滚
按时间清理分区时不能只删物理分区
单纯 DROP PARTITION p2023_01 可能漏掉已软删但未真正删除的数据——比如某条记录 deleted_at = '2023-01-15',它在 p2023_01 里,但业务上你可能想保留“已删除满 6 个月”的记录才清。
性能影响:不分青红皂白删分区,等于把“已软删但未过期”和“从未软删”数据一起扔掉,违反软删除设计初衷。
实操建议:
- 清理前先查:
SELECT COUNT(*) FROM orders WHERE deleted_at IS NOT NULL AND deleted_at ,确认该分区里软删数据是否都满足归档策略 - 用事件调度器(
DB::statement("ALTER TABLE orders TRUNCATE PARTITION p2023_01"))比DROP更安全,不破坏表结构 - 分区名建议带
_trashed后缀,例如p2023_01_trashed,方便区分归档意图
分区键选哪个字段、怎么设边界、软删时间要不要参与分区裁剪——这些不是配置开关能解决的,得看你的查询模式和归档策略是否真正对齐。稍一错位,就变成“以为加速了,其实更慢;以为留住了,其实早丢了”。











