删除权:九张表、九十七行与两个等保留期的副本

用户注销四十天后回来问「真的删了吗」,他才发现 comments、files 与五个外部服务里仍有记录,备份里也有。本文把十二张表分成九张级联、两张按保留期、一张法定保留,用五步流程与三十天加三十天的副本口径,算清每年三十六个账号的删除量。

用户问「删了吗」,你能拿出的不该是一句「删了」,而是一条时间轴。

一、注销四十天后的那封回信

一个用户注销账号四十天后又回来问:「我的数据真的删了吗?」陆远记得自己执行过删除,users 表里那一行确实没了。但他顺着 user_id 去查,comments 还在,files 表里的十八条记录还在,对象存储里的十八个文件也还在;五个外部服务里,邮件列表仍留着这个地址,分析后台还能看到这个人的事件。

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

他这才意识到,自己理解的「删除」只是 DELETE FROM users,而用户理解的「删除」是「所有地方都不再持有」。

删除是一个跨系统动作,不是一条 SQL。 主库、对象存储、外部服务、备份,四处各有各的删除方式与时间常数,少算一处,承诺就不成立。

「已删除」至少有三种状态。 软删是标记不可见但仍持有,硬删是行从主库消失,副本过期是备份与分析明细到期。只做到第一种就回复「已删除」,是把内部状态当成了用户视角。

外键级联只覆盖同一个库。 PostgreSQL 的外键能保证 9 张表跟着 users 一起走,但它管不到对象存储里的文件,也管不到五个外部服务里的记录。

承诺必须写成时间轴,而不是形容词。 「尽快删除」无法验收;「主库 30 天、全部副本 60 天」才能被检查,也才能被写进回复。

二、十二张表与三类处理方式

12 张含个人信息的表不是同一种命运:9 张按 user_id 级联硬删,2 张按保留期清理,1 张因法定要求只能匿名化。

类别表数处理方式规模与说明
按 user_id 级联9软删 30 天后硬删单账号 97 行
按保留期清理2events 90 天、audit_logs 按事件 90 天至 36 个月随事件增长
法定保留1payments 与发票保留 7 年,只匿名化不可删除

9 张级联表是 users、profiles、organizations、memberships、invitations、projects、files、comments、messages。它们的共同点是都带 user_id,能在同一个事务里按人清掉。

97 行是九张表加起来的行数,不是一个账号在每张表各有一行。有的账号只在 users 与 profiles 留痕,有的账号建过项目、传过文件、写过评论,行数会集中在后几张表;删除时按 user_id 扫一遍,不能假设每张表都是 0 或 1。

2 张按保留期清理的是 events 与 audit_logs。events 走 90 天分析明细口径;audit_logs 不能统一写成一个数字,登录失败 90 天、删除事件 36 个月、账单事件 7 年,按事件类型分别到期。

1 张法定保留的是 payments 与发票。它不能删,只能匿名化:去掉 user_id 与邮箱,保留金额、日期与发票号,满足 7 年留存要求。

所以「删一个账号」在数据库里是 9 张表,在时间上是 30 天,在外部是 5 个接口,在账上还有 1 张不能删的表。把这几件事分开登记,才不会在回复用户时把「已匿名化」说成「已删除」。

三、五步流程与五个外部接口

删除流程固定五步,顺序不能换:

  1. 验证身份,沿用访问请求的三问核验。
  2. 软删 30 天,账号不可登录,但数据仍持有。
  3. 硬删 9 张表,单账号 97 行。
  4. 调 5 个外部删除接口,留存回执。
  5. 等 2 个保留期,分析明细 90 天、备份 30 天。

7 个外部处理者里,5 个提供删除接口,调用后能拿到回执;其余只能停用账号,等对方自己的保留期到期。

外部处理者数量删除方式
提供删除接口5调接口并保存回执
只能停用2停用账号,等其保留期

软删期仍然算持有。用户在这 30 天里反悔可以恢复,所以这段时间不能对外说「已经删除」,只能说「已停用,30 天后清除」。

身份核验在删除请求上比访问请求更重要。删错人的代价不可逆,所以三问核验之外,还要在软删窗口内保留一次可撤销的机会;核验不通过就不进入删除队列。

硬删必须排在软删之后,不能提前。提前硬删会让「30 天冷静期」变成一句空话,也会让误删无法回滚。

外部接口要留下回执:调用时间、返回码、对方单号。没有回执的删除,在复盘时等于没做。

两个保留期是承诺里最容易漏掉的部分。备份的 30 天从备份生成日算起,不是从删除日算起,所以最坏情况下副本要到第 60 天才消失;分析明细的 90 天同理,只能等。

四、三十天加三十天与一年的删除量

对外的删除承诺写成两段:主库 30 天,全部副本 60 天。前一段是软删窗口,后一段是软删 30 天加备份 30 天,30 + 30 = 60。

对象单账号36 个账号/年
9 张级联表97 行3,492 行
文件18 个648 个
事件452 条/年16,272 条

复算:

年删除占比:36 ÷ 344 = 10.465% ≈ 10.5%
级联行数:97 × 36 = 3,492 行
文件数:18 × 36 = 648 个
事件数:452 × 36 = 16,272 条
副本口径:软删 30 天 + 备份 30 天 = 60 天

一年删 36 个账号,只占 344 个账号的 10.5%,看起来不多;但每个账号都要走完 9 张表、18 个文件、5 个接口和 2 个保留期,任何一个环节漏掉,回复就不成立。

10.5% 是账号口径,不是行数口径。一个重度账号可能带走上百行与几十个文件,一个只注册未使用的账号可能只有几行;按账号数排期,按实际行数与文件数准备清理窗口,两者不能互相替代。

发票的匿名化要单独记账:删掉 user_id 与邮箱,保留金额、日期、发票号,7 年后由清理任务统一处理。这部分不能算进「已删除」,只能算「已匿名化」。

删除日志本身也要最小化。只记请求编号、时间、操作人、结果与回执号,不要为了「可追溯」把被删的内容再抄一份进日志,那等于用日志重建了一份副本。

每年抽 3 个已删除账号做验证:查 9 张表是否为零行、文件是否 404、5 个接口是否有回执、备份是否已过期。抽 3 个不多,但足以发现流程在哪一步断掉。

五、行动清单与复算验收

把删除从一条 SQL 拆成一条时间轴,用下面八条确认每个环节都有人负责、有证据可查。

清单按「表、窗口、接口、账本、抽检」五类排:前三类保证删得掉,后两类保证说得清。每完成一项就在工单里留一条日期,年底复盘时才能看出哪一步平均耗时最长。

  • 列出 12 张含个人信息的表,写清表名与字段数
  • 标出 9 张级联、2 张按保留期、1 张法定保留
  • 在 9 张表上写清 user_id 外键与级联规则
  • 软删固定 30 天,期间对外只说「已停用」
  • 对接 5 个外部删除接口,保存调用回执
  • 把分析 90 天与备份 30 天写进承诺,合计 60 天
  • 发票只做匿名化,保留 7 年并单独记账
  • 每年抽 3 个已删除账号,逐项验证 9 表、文件与回执

验收标准:任取一个已删除账号,能在 60 天内拿出 9 张表为零、18 个文件不可访问、5 个接口有回执的证据链。

使用边界与实操验收

边界一:删除不是一键。
它至少涉及主库、对象存储、外部服务与备份四类系统,任何一类没覆盖,都不能对外说「已删除」。

边界二:备份与法定保留是例外。
备份 30 天、发票 7 年都属于不能立即清除的情形,要在回复里写清范围与到期时间。

边界三:外部服务可能只支持停用。
7 个处理者里只有 5 个提供删除接口,其余只能停用账号,效果是「不再处理」,不是「数据消失」。

边界四:软删期仍算持有。
30 天冷静期里数据还在,此时收到访问或导出请求,仍要按持有中的数据来响应。

边界五:删除日志本身要最小化。
日志只记请求编号、时间、操作人、结果与回执号,不复制被删内容,避免制造新的副本。

关键参考与核验范围

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

系列导航

继续阅读

探索更多技术文章

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

全部文章 返回首页

「Indie」更多文章

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