数据库无关联设计,删除数据
一、为什么不直接物理删除
即使业务上"不需要保留"订单数据,直接 DELETE 仍会带来问题:
场景:某电商决定只保留3个月订单,过期删除
直接 DELETE 的问题:
| 问题 | 说明 | 实际影响 |
|---|---|---|
| 主从延迟 | 大删除产生大量 binlog | 从库延迟几分钟到几十分钟 |
| 锁竞争 | DELETE 期间锁住大量行 | 影响正常读写,可能超时 |
| 磁盘碎片 | 删除后空间不释放,只是标记可复用 | 表文件大小不变,浪费空间 |
| 无法恢复 | 误删后只能从备份恢复 | 恢复时间几小时到几天 |
| 事务日志膨胀 | undo log 记录删除操作 | 可能撑爆磁盘 |
| 缓存穿透 | 大量删除导致 Buffer Pool 污染 | 影响后续查询性能 |
二、标准做法:多阶段生命周期
完整的数据流转路径
订单创建 → 热数据(3个月) → 温数据(1年) → 冷数据(3年) → 归档/删除
↓ ↓ ↓ ↓ ↓
在线库 在线库 历史库 冷存储 合规销毁
可读写 可读写 只读查询 离线查询 物理删除
实际表设计:状态字段
# orders 表增加状态字段
orders 表字段:
- id
- user_id
- user_name
- total_amount
- status # 业务状态:待支付/已完成/已取消
- data_status # 数据生命周期状态:热/温/冷/已归档
- created_at
- archived_at # 归档时间
- deleted_at # 标记删除时间
数据状态流转示例:
| 订单ID | 创建时间 | data_status | deleted_at | 说明 |
|---|---|---|---|---|
| 1001 | 2024-11-01 | 热 | NULL | 3个月内,在线库可读写 |
| 1002 | 2024-08-15 | 温 | NULL | 3-12个月,在线库只读 |
| 1003 | 2023-06-01 | 冷 | NULL | 1-3年,已迁移到历史库 |
| 1004 | 2022-01-01 | 已归档 | 2025-01-01 | 超过3年,已归档并标记删除 |
三、不同场景的处理策略
场景1:订单数据完全不需保留(如测试数据、临时购物车)
策略:定时任务批量删除 + 软标记
# 每天凌晨执行
def cleanup_temp_carts():
# 步骤1:标记(软删除)
db.execute("""
UPDATE shopping_carts
SET status = '已删除',
deleted_at = NOW()
WHERE status = '临时'
AND created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)
LIMIT 10000
""")
# 步骤2:延迟物理删除(可选,7天后真正删除)
db.execute("""
DELETE FROM shopping_carts
WHERE status = '已删除'
AND deleted_at < DATE_SUB(NOW(), INTERVAL 7 DAY)
LIMIT 5000
""")
数据状态:
| id | user_id | status | created_at | deleted_at |
|---|---|---|---|---|
| 1 | 100 | 正常 | 2024-02-10 | NULL |
| 2 | 101 | 已删除 | 2024-02-01 | 2024-02-05 |
| 3 | 102 | 已删除 | 2024-01-20 | 2024-01-25 |
场景2:订单数据需要保留但不可见(用户注销场景)
策略:更新状态 + 数据脱敏
# 用户注销时
def anonymize_user_orders(user_id):
# 不删除,只脱敏和标记
db.execute("""
UPDATE orders
SET user_name = '[已注销]',
user_phone = NULL,
user_address = NULL,
is_visible = 0, # 前端不可见
data_status = '已脱敏'
WHERE user_id = ?
""", user_id)
订单数据变化:
| id | user_id | user_name | total_amount | is_visible | data_status |
|---|---|---|---|---|---|
| 1001 | 3 | 王五 | 1299.00 | 1 | 正常 |
| 1002 | 3 | [已注销] | 599.00 | 0 | 已脱敏 |
效果:
- 后台对账、财务审计仍能看到完整数据
- 前端不展示给用户
- 满足合规要求(如 GDPR 的"被遗忘权")
场景3:订单数据需要归档清理(节省在线库空间)
策略:分区表 + 在线 DDL 删除分区
MySQL 分区表示例:
-- 按月分区
CREATE TABLE orders (
id BIGINT,
created_at DATETIME,
...
) PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
PARTITION p202403 VALUES LESS THAN (202404),
...
);
删除过期数据:
-- 直接删除整个分区(毫秒级完成,不产生大量 binlog)
ALTER TABLE orders DROP PARTITION p202301;
ALTER TABLE orders DROP PARTITION p202302;
ALTER TABLE orders DROP PARTITION p202303;
数据流转:
- 热数据:最近3个月分区,在线库可读写
- 温数据:3-12个月分区,在线库只读查询
- 冷数据:1年前分区,迁移到历史库后删除原分区
场景4:订单数据需要彻底物理删除(合规要求)
策略:专用清理任务 + DBA 审批
# 合规删除流程
def compliant_delete():
# 1. 标记待删除
db.execute("""
UPDATE orders
SET delete_flag = 1,
delete_request_time = NOW(),
delete_reason = '用户主动删除'
WHERE user_id = ?
AND status = '已完成'
AND created_at < DATE_SUB(NOW(), INTERVAL 3 YEAR)
""")
# 2. 进入审批队列(等待 DBA 审核)
create_approval_task(user_id, "合规删除申请")
# 3. 审批通过后,执行物理删除(低峰期)
# 先备份,再删除
export_to_archive(user_id) # 先归档到冷存储
db.execute("DELETE FROM orders WHERE delete_flag = 1")
四、决策树:何时物理删除 vs 标记删除
是否需要物理删除?
│
├─ 有合规要求(GDPR、金融法规)
│ └─ 是 → 物理删除(但先归档备份)
│
├─ 数据量极大,影响性能
│ └─ 是 → 分区表,直接 DROP PARTITION
│
├─ 数据可恢复要求高
│ └─ 是 → 标记删除,永不物理删除
│
├─ 成本考虑(存储费用高)
│ └─ 是 → 迁移到廉价冷存储后物理删除
│
└─ 默认策略
└─ 标记删除 + 延迟清理(7-30天后物理删除)
五、实际案例对比
案例A:淘宝订单
- 策略:永不物理删除
- 方式:状态字段 + 分区表
- 原因:交易凭证,可能需要追溯10年
| 订单状态 | 数据状态 | 用户可见 | 后台可见 |
|---|---|---|---|
| 已完成 | 热数据 | 是 | 是 |
| 已删除(用户侧) | 冷数据 | 否 | 是(运营可查) |
| 已归档 | 归档库 | 否 | 是(需申请) |
案例B:临时购物车
- 策略:7天后物理删除
- 方式:定时任务批量删除
- 原因:无保留价值,节省空间
| 创建时间 | 操作 | 数据状态 |
|---|---|---|
| 0-7天 | 正常使用 | 在线库 |
| 7-14天 | 标记"已过期" | 在线库(不可见) |
| 14天后 | 物理删除 | 已删除 |
案例C:用户聊天记录
- 策略:软删除 + 定期归档
- 方式:状态字段 + 历史库
- 原因:可能恢复,合规要求
| 时间 | 状态 | 存储位置 |
|---|---|---|
| 删除时 | 标记"已删除" | 在线库 |
| 30天后 | 迁移 | 历史库 |
| 1年后 | 压缩归档 | 冷存储 |
| 3年后 | 物理删除 | 无 |
六、总结
| 数据重要性 | 推荐策略 | 方式 |
|---|---|---|
| 核心交易数据 | 永不删除 | 状态字段 + 分区 + 归档 |
| 用户主动删除 | 软删除 | 状态字段 + 脱敏 |
| 临时数据 | 延迟物理删除 | 定时任务 + 分批 |
| 日志/监控数据 | 定期清理 | 按时间分区删除 |
| 合规要求删除 | 物理删除 | 审批流程 + 备份先行 |
核心原则:
- 默认软删除:用状态字段标记,保留恢复可能
- 物理删除是最后手段:只有明确合规要求或成本压力才执行
- 分批操作:任何删除都要限流、分批,避免影响业务
- 先备份后删除:物理删除前务必归档备份
- 分区表优先:按时间分区,删除分区比 DELETE 高效百倍
即使"不需要保留"的订单数据,大公司也会先标记删除,观察一段时间(如30天),确认无问题后再分批物理删除。这是对数据安全的敬畏。
静,静,静

浙公网安备 33010602011771号