本文目录导读:

为PHP项目制定数据过期规则,通常需要结合业务逻辑、存储成本、合规要求和性能来综合设计,以下是系统化的制定步骤和常见策略,附带PHP代码示例。
核心决策维度
-
数据性质
- 临时数据(验证码、Session、URL令牌):通常几分钟到几天。
- 日志数据(操作日志、访问日志):保留30天~1年。
- 用户行为数据(购物车、浏览历史):保留7天~6个月。
- 敏感数据(欧盟GDPR、中国《个人信息保护法》要求的数据):按法律要求的最小化保存时间。
-
可容忍的延迟
- 实时删除(如支付倒计时到期的订单)→ 主动触发删除。
- 批量清理(如日志归档)→ 定时任务(Cron)。
-
数据量级
- 千万级以下:直接DELETE。
- 亿级以上:考虑分区表(按时间分区,直接DROP分区)或TTL(Time To Live)索引(如MongoDB、ClickHouse)。
过期规则的常见模式
| 规则类型 | 适用场景 | 实现方式(PHP+MySQL) |
|---|---|---|
| 固定时间淘汰 | 验证码、临时Token | 存储created_at,查询时过滤 WHERE created_at < NOW() - INTERVAL 10 MINUTE,并定时清理 |
| 基于活动时间 | 用户session、在线状态 | 存储last_activity,超过N天未活动视为过期 |
| 基于状态+时间 | 订单(待付款24h未付自动取消) | WHERE status='pending' AND created_at < NOW() - INTERVAL 24 HOUR |
| 软删除+物理清理 | 回收站、历史记录 | 标记deleted_at,30天后物理删除 |
| 批量归档 | 10亿级日志 | 按日期分区,定时 ALTER TABLE logs DROP PARTITION p20240101 |
实现过期规则的PHP方案
单条数据过期(实时查询+后台清理)
// 1. 查询时自动过滤过期数据(推荐用于业务逻辑)
public function getValidCode(string $phone): ?string
{
$stmt = $pdo->prepare(
"SELECT code FROM verification_codes
WHERE phone = :phone AND expired_at > NOW()
LIMIT 1"
); // expired_at 是预先计算好的绝对过期时间
$stmt->execute(['phone' => $phone]);
return $stmt->fetchColumn();
}
// 2. 定时任务清理(每天凌晨执行)
// 在 crontab 中设置:0 3 * * * php /path/to/cleanup.php
// cleanup.php
require 'bootstrap.php';
$pdo->exec("DELETE FROM verification_codes WHERE expired_at < NOW() - INTERVAL 1 DAY"); // 保留一天内的过期记录用于审计
echo "清理完成";
分区表策略(适合海量日志)
-- 建立按月分区表
CREATE TABLE access_logs (
id BIGINT UNSIGNED AUTO_INCREMENT,
user_id INT UNSIGNED,
url VARCHAR(500),
created_at DATETIME,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (TO_DAYS(created_at)) (
PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
// PHP 定时任务:每个月1日删除6个月前的分区
$sixMonthsAgo = new DateTime('-6 months');
$partitionName = 'p' . $sixMonthsAgo->format('Ym');
$pdo->exec("ALTER TABLE access_logs DROP PARTITION {$partitionName}");
// 注意:需要先添加新分区,避免数据写入失败
基于TTL的数据库特性(最低成本)
- MySQL 8.0+ 可结合
Event Scheduler+ 存储过程,但通常建议用Cron。 - Redis 原生支持TTL:PHP只用维护过期字段或利用Redis过期回调。
// Redis 验证码5分钟过期
$redis->setex("sms_code:{$phone}", 300, $code);
// 或利用MySQL 8.0的“不可见索引” + 定时任务,但不如Redis方便。
常见陷阱与最佳实践
-
不要依赖PHP进程内定时器
用Cron(Linux)或任务调度系统(如Laravel Scheduler)替代sleep()循环,避免内存泄漏。 -
分批删除避免锁表
对几百万行数据清理时,每次只删除1000行并短暂sleep。while (true) { $affected = $pdo->exec("DELETE FROM logs WHERE created_at < '2023-01-01' LIMIT 1000"); if ($affected === 0) break; usleep(50000); // 50ms } -
索引策略
确保expired_at、created_at、last_activity有索引,否则清理或查询会导致全表扫描。 -
容灾与回滚
清理前备份或采用软删除。$pdo->beginTransaction(); $pdo->exec("INSERT INTO logs_backup SELECT * FROM logs WHERE created_at < ..."); $pdo->exec("DELETE FROM logs WHERE created_at < ..."); $pdo->commit(); -
符合GDPR等法规
如果数据标记为“可被遗忘”,需要提供物理删除接口,并在删除后断开业务关联。
综合示例:订单过期自动取消
// 规则:状态为pending(待支付)且 created_at 超过24小时 ⇒ 自动取消
// 实现方案:每次查询订单时,顺便检查并取消过期的,但更好的做法是定时任务
// 定时任务(每分钟执行一次)
$stmt = $pdo->prepare(
"UPDATE orders
SET status = 'cancelled', cancelled_reason = 'payment_timeout'
WHERE status = 'pending' AND created_at < NOW() - INTERVAL 24 HOUR"
);
$stmt->execute();
echo "Cancelled {$stmt->rowCount()} orders.\n";
制定规则时的业务确认清单
- [ ] 该数据最长可以保留多久?(法律、存储成本)
- [ ] 如果提前删除,用户是否受影响?(例如购物车内容)
- [ ] 过期后是否还需要统计用途?(需要保留脱敏或聚合数据)
- [ ] 是否需要通知用户数据即将被清理?(例如GDPR要求的“数据销毁通知”)
- [ ] 清理频率如何?实时 vs 批量?高峰期能不能执行?
| 场景 | 推荐规则 | 实现方式 |
|---|---|---|
| 验证码/临时令牌 | 固定时间(5~30分钟) | Redis TTL / MySQL WHERE expired_at |
| 用户Session | 最后一次活动后1~7天 | Laravel Redis Session + GC |
| 操作日志 | 保留90天,物理删除 | 分区表 DROP PARTITION |
| 软删除回收站 | 保留30天后物理删除 | 定时 DELETE LIMIT |
| 敏感数据(GDPR) | 按法规+用户请求立即删除 | 物理删除 + 备份清除 |
如果你能提供具体的业务场景(例如是电商订单、用户行为日志还是内容审核),我可以给出更精确的规则建议和SQL示例。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播