保留期:六类数据、四个清理任务与一次没跑的备份清理

季度账单里对象存储涨了 10.8 GB,查下来是备份清理任务连续 12 天失败,没人看告警。保留期不是磁盘够不够的问题:六类数据各有期限与起算点,四个清理任务每天各跑一次,失败 0.8% 也要有人补跑、记账,每月对一次账。

数据「留着」不会立刻出事,但它每天都在计费,也每天都在扩大泄露面。

一、涨了 10.8 GB 的季度账单

十月第一个周一,他打开季度账单,对象存储那一行比上季度多了 10.8 GB。按 0.9 GB/天的备份增量算,正好是 12 天——不是这 12 天多存了数据,是 12 天的旧备份一直没被删掉。

以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。

他先怀疑是用户上传变多,翻了 files 表,18 个文件/账号的口径没有变化;再去看备份目录,时间戳停在 12 天前的那一批,之后每天都在写新的,旧的却一直躺在那里。

清理任务不是没跑,是跑了以后失败。对象存储的批量删除要先列举再删除,列举分页在某个前缀上超时,任务以非零码退出,告警发到了一个长期没人打开的邮箱。12 天里,任务日志上躺着 12 条一模一样的错误。

荒诞的地方在于:他花了两个下午设计「删除权」的流程,却没有为「到期自动删除」设一个能叫醒人的告警。删除请求一年 3 件,清理任务每天 4 次——真正高频的那条路径,反而最没有人看。

他最初把这件事归因于「备份变大了」,可 0.9 GB/天 的增量一直稳定,真正变化的只是删除侧。第二个反应是手动删掉,10.8 GB 确实下去了,但下个月同一个前缀会不会再超时,没有任何机制保证——手工修复只能救一次事故,救不了流程。

最后他做的是把「任务有没有跑成功」变成一个能看的数字:每天 4 条记录,成功、失败、删除行数、耗时。数据「留着」的成本因此第一次出现在账单之外的页面上。

这件事之后,他把季度账单里的对象存储单列一行,每月只看斜率。体积本身不说明问题,斜率突然变陡才说明某条删除路径断了。

这一篇讲六类数据各自的期限、四个每天要跑的清理任务,以及一次失败 12 天都没人发现的告警该怎么补。

二、六类数据,六个期限

保留期不能只写一个数字。同样是个人信息,账号主数据、日志、分析明细、备份、审计、发票的期限来自完全不同的理由:反悔窗口、排障需要、产品度量、灾难恢复、举证责任、法定义务。

数据类保留期起算点期限来自
账号主数据删除后 30 天用户确认删除反悔窗口与误删恢复
应用日志90 天写入当天排障需要
分析明细90 天事件发生产品度量口径
备份30 天备份生成灾难恢复
审计日志90 天–36 个月事件发生按事件类型分档
发票7 年开票日法定义务

审计日志那一行最容易被写错。它不是「统一保留 365 天」,而是按事件分档:登录失败 90 天、删除操作 36 个月、账单相关 7 年。三档混成一个数字,等于把「该早删的留着」和「该留的删了」同时犯下。

发票 7 年是唯一不能靠删除解决的:它必须留,但不必留得能认出人。到期后把 user_id 与邮箱替换成不可逆的标记,金额、日期、税号保留,因为财务与税务要的是交易,不是身份。

六类数据的期限还要写清起算点。账号主数据的 30 天从「用户确认删除」起算,不是从「最后一次登录」;备份的 30 天从「备份生成」起算,不是从「源数据删除」。起算点写错,下游所有删除承诺都会跟着错。

应用日志与分析明细都是 90 天,理由却不同:前者为了排障,后者为了度量。理由不同,意味着其中一类可以缩短而另一类不能,不能因为数字相同就合并成一条策略。

软删 30 天加备份 30 天,最坏情况是 60 天。这个数字要出现在对用户的删除承诺里,而不是只说「30 天」——承诺写的是主库,用户理解的是全部。

期限最终要落到字段,而不是整张表。users 有 9 个字段,邮箱与姓名的期限未必相同;按表设期限最省事,也最容易一刀切切错。

三、四个任务,每月 120 次

六类数据落到执行层,是四个每天跑一次的清理任务:日志清理、分析明细清理、软删到期硬删、备份轮转。4 个 × 30 天 = 120 次/月,这个数既是工作量,也是告警该盯的分母。

任务记录至少要有四个字段:任务名、开始时间、删除条数、退出码。少了删除条数,你只知道它跑过,不知道它干了什么;少了退出码,失败会被记成成功,而「跑过」与「跑成」是两件事。

任务删什么频率失败后果
日志清理90 天前的应用日志每日存储增长、排障数据超期
分析清理90 天前的分析明细每日明细表膨胀、度量口径失真
软删清理30 天前软删的 9 张表每日用户以为删了其实还在
备份轮转30 天前的备份对象每日存储成本上升、副本超期

0.8% 的失败率意味着 120 × 0.008 = 0.96 次/月,约 11.52 次/年。单看比例不高,但失败往往集中在同一类对象上——某个前缀、某个体积较大的分片——所以「平均 0.96 次」在真实系统里更常见的形态是「连续 12 天都失败」。随机失败和系统性失败要分开看,后者的告警阈值不能按平均值设。

告警阈值也该按任务分。备份轮转连续失败 2 次就该叫人,日志清理失败 1 次可以等第二天补跑;四条任务共用一个阈值,结果不是太吵就是太安静,两种都会让人关掉通知。

失败处理只有两步:补跑、记账。补跑要幂等,按「删除条件」重跑,而不是按「上次删到哪」续跑;记账要写进审计日志——哪个任务、删了多少行、第几次重试。审计日志本身也有保留期,登录失败 90 天这条同样适用。

补跑要有上限。同一任务连续补跑 3 次仍失败,应该停下来告警,而不是无限重试——把对象存储的请求配额打满,比一次删除失败更麻烦。

四个任务还要共用同一个「到期」定义:到期指起算点加保留期得到的时刻已经过去,而不是「等到月末批量处理」。按月末处理,最坏会多留将近一个保留周期,而账单上的斜率会替你把这件事说出来。

软删清理最容易出事。9 张表按 user_id 级联,删一个账号要确认 9 张表都干净;漏一张,用户下次登录还能看到残留。删除顺序按外键依赖从子到父,或者直接交给数据库的级联约束,别在应用层手写 9 条 DELETE。

备份轮转是四个任务里唯一删副本的。它不减少主库任何一行,只决定灾难恢复能回到多久以前。30 天是恢复窗口,不是数据寿命;把两者混为一谈,就会出现「主库删了、备份还留着」的承诺缺口。

四个任务还该有一条总账:今天到期多少条、实际删了多少条、还剩多少条。三个数字对不上,说明有对象被跳过,而不是「删完了」。

四、按用途定期限,不按磁盘定

磁盘快满时,最自然的反应是「删点旧的」。这是最危险的排序方式:它会把还在举证期内的审计日志和已经超期的分析明细一起清掉。保留期应当由用途倒推,磁盘只决定要不要扩容。

法定例外必须单列。发票 7 年是硬约束,任何「一键删号」的流程都要绕开 payments 表,只做匿名化。这一条要写进代码而不是文档——文档会被忘记,外键约束不会。

保留期表还要能回答「为什么不是 60 天」。每个数字后面都该有一句理由:90 天对应一个季度的排障窗口,30 天对应一次误删恢复,7 年对应法定义务。说不出理由的期限,迟早会被下一个人改掉。

删除与匿名化要分成两条路径写。删除是不可逆的消失,匿名化是保留交易结构、去掉可识别性;发票走后者,账号主数据走前者,两条路径复用一个函数,早晚会有人把该留的删了。

备份还有一个隐蔽的限制:留 30 天意味着只能回到 30 天内的任意一天,超过 30 天的误删恢复不了。这个限制要写进运维手册,而不是等出事时才发现恢复窗口早已滑走。

每季度抽 10 条到期记录手工核对:创建时间、到期时间、实际删除时间是否对得上。抽查不是为了抓 bug,是为了证明「到期删除」真的在发生,而不只是配置里写着。

保留期与访问控制是两件事。数据到期前要能被正确的人读到,到期后连管理员也不该在常规路径里看到;把「删不掉」当成「权限没问题」,是另一种混淆。

复算:把任务量、失败率与备份体积各算一遍,四个数字互相独立,但要能同时对得上。

清理任务:4 个 × 30 天 = 120 次/月
失败:120 × 0.008 = 0.96 次/月;0.96 × 12 = 11.52 次/年
备份体积:0.9 GB/天 × 30 天 = 27 GB(稳态)
漏跑 12 天:0.9 GB/天 × 12 天 = 10.8 GB(账单上多出来的这批)

27 GB 是稳态体积:每天新增 0.9 GB、每天删掉第 31 天的那份,进出相等。10.8 GB 是事故的瞬时增量,所以账单不是缓慢上涨,而是台阶式跳一下。理解这一点,才能判断「体积异常」该看绝对值还是看斜率。

五、行动清单与复算验收

清单按「先能看见,再能补跑,最后才谈自动化」排序。顺序反了,自动化只会更快地掩盖失败。

  • 把六类数据的保留期、起算点、依据写成一张表,放进代码仓库而不是文档站
  • 为四个清理任务各建一条日任务记录,成功与失败都落库
  • 失败告警发到会看的渠道,连续失败 2 次升级为即时消息或电话
  • 补跑脚本按删除条件幂等重跑,不依赖上次进度
  • 软删清理按外键顺序删 9 张表,或直接使用数据库级联
  • 备份轮转与主库保留期分开配置,30 天恢复窗口写进删除承诺
  • 发票等法定保留数据只匿名化,代码里禁止对 payments 执行 DELETE
  • 每月对账一次:实际删除量、任务失败次数、存储体积三者对得上

验收标准: 随机挑一天,能说清四个任务是否都跑过、失败几次、补跑后删了多少行,以及当天存储体积为什么是那个数。

使用边界与实操验收

边界一:保留期不等于备份。 主库删除后 30 天是数据寿命,备份 30 天是恢复窗口,两者起算点不同、责任不同,不能合并成一句「我们保留 30 天」。

边界二:审计日志按事件类型分档。 登录失败 90 天、删除 36 个月、账单 7 年,三档来自不同的举证需要;统一写成 365 天,既不满足长的,也超过了短的。

边界三:清理任务会误删。 任何自动删除都该先有 dry-run 与软删缓冲,尤其是按条件批量删除的任务;删除条件写错一次,代价不是重跑,而是不可恢复。

边界四:法定保留优先于用户请求。 发票 7 年不能因为用户注销而提前删除,只能匿名化;这条冲突要在删除流程里显式解释,而不是静默跳过。

边界五:跨区域副本各有保留期。 主库在 us-east-1、备份在 eu-central-1,两地的任务、告警与对账要分别做;只盯一个区域,另一个区域的超期数据不会被任何人发现。

关键参考与核验范围

  • GDPR Art 5 :储存限制原则的原文,用于核对「保留期必须有依据、不得无限期保存」这一条。
  • GDPR Art 30 :处理活动记录的要求,保留期与删除方式属于应记录的信息。
  • S3 生命周期 :对象按前缀与天数自动过期的机制,对应备份轮转任务的实现方式。
  • PostgreSQL 外键约束 :级联删除与 RESTRICT 的语义,用于核对 9 张表的删除顺序与依赖。
  • NIST 隐私框架 :把「保留与处置」放进整体隐私治理的参考框架。

以上链接于 2026-10-26 实测可公开访问;未核验版本、价格与配额。核验日期:2026-10-26。

系列导航

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Indie」更多文章

  1. 开源项目验收:十项能力、四维评分与三种不该开源的项目
  2. 治理、fork 与退出:17 位贡献者、82% 的提交与一次改协议的代价
  3. 双许可与企业版:九家询价、两家成交与 4,800 美元