软删除与硬删除:选择正确的数据生命周期
软删除与硬删除:学习如何保留历史记录、避免引用断裂,同时通过明确规则满足隐私删除要求。

什么是软删除和硬删除\n\n“删除”可以有两种非常不同的含义。混淆它们会让团队丢失历史记录或无法满足隐私请求。\n\n硬删除 是大多数人想象中的删除:行从数据库中被移除,之后查询就找不到它了。这是真正的移除,但除非你有针对性设计,否则也可能破坏引用(例如订单指向已删除的客户)。\n\n软删除 则保留该行,但把它标记为已删除,通常用像 deleted_at 或 is_deleted 这样的字段。你的应用把它当成已删除处理,但数据仍然存在以供报表、支持和审计使用。\n\n“软删除 vs 硬删除”的权衡很简单:历史记录 vs 真正移除。软删除保护历史并允许“撤销”。硬删除则减少存储的数据量,这在隐私、安全和法律规则上很重要。\n\n删除影响的不仅是存储。它改变了团队以后能回答的问题:支持人员想了解过去的投诉,财务核对账单,合规检查谁在何时更改了什么。如果数据过早消失,报表会改变、总额不再匹配、调查变成猜测。\n\n一个有用的心智模型:\n\n- 软删除在日常视图中隐藏记录,但为可追溯性保留它。\n- 硬删除永久移除记录,最小化存储的个人数据。\n- 许多实际应用同时使用两者:保留业务记录,在需要时移除个人标识字段。\n\n在实践中,你可能会先软删除用户账户以阻止登录并保留订单历史,然后在保留期后或在经验证的 GDPR 删除权 请求下对个人字段执行硬删除或匿名化。\n\n没有工具能替你做出决定。即便在像 AppMaster 这样的无代码平台上构建,真正的工作仍然是为每张表决定“已删除”意味着什么,并确保每个界面、报表和 API 遵循相同规则。\n\n## 删除在日常应用中造成的真实问题\n\n大多数团队只有在出现问题时才会注意到删除。一条“简单”的删除可能会抹去上下文、历史,削弱你解释事件的能力。\n\n硬删除风险很大,因为它难以撤销。有人点错按钮、自动任务有 bug,或支持人员按错流程。如果没有干净的备份与明确的恢复流程,丢失就是永久的,而业务影响会迅速显现。\n\n断裂的引用是下一个惊喜。你删除一个客户,但他们的订单仍然存在。现在订单指向空的引用,发票无法显示账单姓名,门户在加载关联数据时报错。即使有外键约束,所谓的“修复”也可能更糟:级联删除会抹掉比预期更多的数据。\n\n分析与报表也会变得混乱。旧记录消失时,指标会发生回溯性变化。上个月的转化率变化,客户终身价值下降,趋势线出现无法解释的缺口。团队开始为数字争论,而不是做决策。\n\n支持和合规方面受伤最深。用户问“为什么我被收费?”或“谁改了我的套餐?”如果记录不存在,你无法重建时间线。你失去了能回答“谁、何时、为什么更改”的审计轨迹。\n\n关于软删除 vs 硬删除的一些常见失败模式:\n\n- 因意外删除或自动化 bug 导致的永久丢失\n- 缺失的父记录让子记录(订单、工单)成为孤儿\n- 历史行消失导致报表变化\n- 缺乏历史导致支持无法解答问题\n\n## 何时将软删除作为更好的默认选项\n\n当一条记录具有长期价值或与其他数据相关联时,软删除通常是更安全的选择。你不是移除行,而是给它打上已删除的标记(例如 deleted_at 或 is_deleted),并在常规视图中隐藏它。在软删除 vs 硬删除 的决策中,这个默认往往能降低后续的意外风险。\n\n它在需要数据库审计轨迹的场景中特别有用。运维或业务团队经常需要回答“谁更改了这个订单?”或“为什么这张发票被取消?”之类的问题。如果你过早硬删除,可能会丢失对财务、支持和合规报表重要的证据。\n\n软删除也使“撤销”成为可能。管理员可以恢复被误关闭的工单、重新上架被归档的产品,或在错误的垃圾信息判定后恢复用户生成内容。若数据物理删除,这类恢复很难实现。\n\n关联关系是另一个重要原因。硬删除父行可能会破坏外键约束或在报表中留下令人困惑的空白。使用软删除,连接保持稳定,历史总计(每日收入、已完成订单、响应时间统计)也更一致。\n\n对业务记录(如支持工单、消息、订单、发票、审计日志、活动历史和用户资料)来说,将软删除作为默认是很稳妥的(至少在确认最终删除之前)。\n\n示例:支持人员“删除”一条包含错误的订单备注。用软删除,这条备注在常规 UI 中消失,但在投诉处理时主管仍能查看,财务报表也仍能解释对应变更。\n\n## 何时必须使用硬删除\n\n软删除对很多应用是个好默认,但有时保留数据(即便隐藏)是错误的选择。硬删除意味着记录被真正移除,在法律、安全或成本方面有时是唯一合适的选项。\n\n最明确的情况是隐私和合同义务。如果用户行使了GDPR 删除权,或你的合同承诺在设定期限后删除数据,“标记为已删除”通常不够。你可能需要删除行、相关副本以及任何可重新指向该人的标识符。\n\n安全也是原因之一。有些数据过于敏感,不应保留:原始访问令牌、密码重置码、私钥、一次性验证码或未加密的密钥。为历史保留这些数据很少值得承担风险。\n\n硬删除对规模也是有意义的。如果你有大量历史事件、日志或遥测表,软删除会悄然增长数据库并拖慢查询。按计划清理可以让系统保持响应并使成本可预测。\n\n硬删除通常适用于临时数据(缓存、会话、草稿导入)、短期安全工件(重置令牌、OTP、邀请码)、测试/演示账户以及只需要聚合统计的大型历史数据集。\n\n一个实用做法是把“业务历史”与“个人数据”分离。例如保留会计用的发票记录,但对识别个人的用户资料字段执行硬删除或匿名化。\n\n如果团队在软删除 vs 硬删除 上争论,用一个简单测试:如果保留这些数据会带来法律或安全风险,硬删除(或不可逆匿名化)应当胜出。\n\n## 如何建模软删除以避免意外\n\n软删除在其行为平稳且可预期时效果最好。目标很简单:记录留在数据库中,但应用的常规部分应表现得像它已被删除。\n\n### 选定一个删除信号,并明确它的含义\n\n常见三种模式为:deleted_at 时间戳、is_deleted 布尔位或状态枚举。许多团队偏好 deleted_at,因为它同时回答了“是否已删除”和“何时被删除”这两个问题。\n\n如果你已经有多种生命周期状态(active、pending、suspended),状态枚举仍然可用,但要把“deleted”与“archived”及“deactivated”区分开。它们的含义不同:\n\n- Deleted:不应出现在常规列表中或可被使用。\n- Archived:为历史保留,但仍在“过去”视图中可见。\n- Deactivated:临时禁用,通常可由用户恢复。\n\n### 在唯一字段问题出现前先处理它们\n\n软删除 vs 硬删除 的争议经常在邮箱、用户名或订单号等唯一字段上崩溃。如果用户被“删除”但他们的邮箱仍在且具唯一性,同一个人就无法重新注册。\n\n两种常见修复方法:要么把唯一性约束仅应用于未删除的行,要么在删除时改写该值(例如追加随机后缀)。哪种方法合适取决于你的隐私与审计需求。\n\n### 明确(并保持一致)过滤规则\n\n决定不同受众能看到什么。常见规则是:普通用户永远看不到已删除记录,支持/管理员用户可查看但有明确标注,导出/报表仅在被请求时包含已删除记录。\n\n不要指望“每个人都会记得加过滤条件”。把规则放在一个地方:视图、默认查询或数据访问层。如果在 AppMaster 中构建,通常意味着把过滤逻辑写进端点与业务流程的取数方式,这样已删除行就不会在新界面中意外重现。\n\n把含义写下来(短的内部说明或模式注释)。当“deleted”、“archived”与“deactivated”同时出现在一次会议时,未来的你会感谢现在的记录。\n\n## 保持引用完整:父、子与连接\n\n删除最常通过关系破坏应用。记录很少是孤立的:用户有订单,工单有评论,项目有文件。软删除 vs 硬删除 的棘手部分是如何在产品表现为“消失”的同时仍保持引用一致。\n\n### 外键:有意选择失败模式\n\n外键能保护你免于断裂引用,但每种选项意味着不同的行为:\n\n- RESTRICT:若存在子项则阻止删除。\n- SET NULL:允许删除,但会把子项的外键置空。\n- CASCADE:自动删除子项。\n- NO ACTION:在很多数据库中与 RESTRICT 类似,但时机可能不同。\n\n使用软删除时,RESTRICT 通常是最安全的默认。你保留行,因此键仍然有效,同时避免子项指向空引用。\n\n### 在关系中实现软删除:隐藏而不使其孤立\n\n软删除通常不改动外键。相反,你在应用与报表中过滤掉已删除的父项。如果客户被软删除,他们的发票仍应能正常关联,但界面不应在下拉中显示该客户。\n\n对于附件、评论与活动日志,决定“删除”对用户意味着什么。有些团队保留外壳但移除高风险部分:若隐私要求,替换附件内容为占位符;将评论标注为来自已删除用户(或对作者进行匿名化);保留活动日志不可变。\n\n连接与报表需要明确规则:是否包含已删除行?许多团队维护两种标准查询:一种“仅活跃”,一种“包含已删除”,以免支持与报表意外隐藏重要历史。\n\n## 逐步设计:同时使用两种方式的数据生命周期\n\n实用策略通常对日常失误使用软删除,对法律或隐私需求使用硬删除。如果把问题简化为单一选择(软删除 vs 硬删除),容易错过中间路径:先保留历史一段时间,再清理必须删除的内容。\n\n### 一个简单的五步计划\n\n先把数据分类。比如“用户资料”是个人数据,“交易”是财务记录,“日志”是系统历史。每类数据需要不同规则。\n\n一个适用于多数团队的短计划:\n\n- 定义数据分组与负责人,明确谁有权批准删除。\n- 设定保留与恢复规则。\n- 决定哪些字段采用匿名化而非删除。\n\n- 增加一个定时清理步骤(先软删除,随后硬删除)。\n- 为每次删除、恢复与清理记录审计事件(谁、何时、做了什么、原因)。\n\n### 用一个场景把它落地\n\n假设客户要求关闭账户。首先软删除用户记录以阻止登录并避免破坏引用;然后对不应保留的个人字段(姓名、邮箱、电话)进行匿名化,同时保留会计所需的非个人交易事实;最后由计划任务在等待期后删除仍然属于个人的数据。\n\n## 常见错误与陷阱\n\n团队遇到麻烦往往不是因为他们选错了方法,而是因为执行不一致。一个常见模式是纸面上有“软删除 vs 硬删除”策略,但实际是“在一个界面隐藏,然后忘记其它地方”。\n\n一个容易犯的错误是你在 UI 隐藏了已删除记录,但 API、CSV 导出、管理员工具或数据同步任务仍然会暴露它们。当“被删除”的客户出现在邮件列表或移动端搜索结果中时,用户会很快注意到。\n\n报表与搜索也是陷阱。如果报表查询未一致过滤已删除行,总额会漂移,仪表盘失去可信度。最糟糕的情况是后台任务重新索引或重发已删除项,因为它们没有应用相同规则。\n\n硬删除也可能过度。一次级联删除可能会抹掉你实际上需要保留以供审计的订单、发票、消息与日志。如果必须硬删除,要明确允许消失的内容和必须保留或匿名化的内容。\n\n唯一性约束在软删除下会造成微妙的问题。如果用户删除账户然后尝试用相同邮箱重新注册,若旧行仍保存邮箱则注册会失败。提前规划这一点。\n\n合规团队会问:你能证明删除发生过且何时发生吗?“我们认为它被删除了”通常无法通过许多数据保留审查。保留删除时间戳、触发者以及不可变的日志条目。\n\n在投产前,对整个表面进行健康检查:API、导出、搜索、报表与后台任务。逐表检查级联,并确认用户在允许重新创建唯一数据(如邮箱或用户名)时不会被阻止。\n\n## 上线前的快速检查清单\n\n在选择软删除 vs 硬删除 之前,验证应用的真实行为而非仅仅模式:\n\n- 恢复应该安全且可预期。 若管理员“取消删除”,记录能否以正确状态返回,同时不会恢复应继续删除的敏感项(如已撤销的访问令牌)?\n- 查询默认隐藏已删除数据。 新界面、导出与 API 不应意外包含已删除行。选一个规则并在各处应用。\n- 引用不会破裂。 确保外键与连接不会产生孤儿记录或半空屏幕。\n- 清理有计划与负责人。 软删除只是计划的一半。定义何时永久删除、谁执行以及哪些情况被排除(例如未结争议)。\n- 删除操作像其他敏感操作一样被记录。 记录谁发起、何时发生以及原因。\n\n然后对隐私路径做端到端测试。你能否在所有副本、导出、搜索索引、分析表与集成中履行 GDPR 擦除权,而不仅仅是主数据库?\n\n一种实用的验证方法是在预发布环境执行一次“删除用户”演练并追踪数据流。\n\n## 示例:在保留账单历史的同时删除用户\n\n客户写信来:“请删除我的账户。”但你也有必须保留的发票以供会计与争议检查。这时软删除 vs 硬删除 就变得实际:你可以移除访问与个人详情,同时保留业务必须保留的财务记录。\n\n把“账户”与“账单记录”分开。账户涉及登录与身份;账单记录涉及已发生的交易。\n\n一个清晰的做法:\n\n- 软删除用户账户,使其无法登录且在常规视图中不可见。\n- 保留发票与付款作为活跃记录,但不再关联个人字段。\n- 对个人数据(姓名、邮箱、电话、地址)进行匿名化,替换为“Deleted User”或相似的中性值加非识别内部引用。\n- 对敏感访问项目(API 令牌、密码哈希、会话、刷新令牌与已记住设备)执行硬删除。\n- 仅保留为合规与支持真正需要的数据,并记录理由。\n\n支持工单与消息常处于中间地带。如果消息内容包含个人数据,可能需要对文本部分进行涂黑、移除附件,并仅保留工单外壳(时间戳、类别、解决状态)以供质量跟踪。如果产品发送过外发消息(邮件/SMS、Telegram),也要移除外发标识,以免再次联系该人。\n\n支持人员通常能看到什么?发票号、日期、金额、状态以及用户已删除及删除时间的注释。看不到的则应包括任何可识别个人的信息:登录邮箱、全名、地址、保存的支付方式详细信息或活跃会话。\n\n## 后续步骤:先定规则,然后一致实施\n\n当删除决策被写下来并在整个产品中一致实施时,它们才会生效。把 软删除 vs 硬删除 当作策略问题先解决,而不是一个编码技巧。\n\n先起草一份任何团队成员都能读懂的数据保留策略。它应说明你保留什么、保留多长时间以及为什么。理由很重要,因为它能在两个目标冲突时(例如支持历史 vs 隐私请求)告诉你哪一方优先。\n\n一个不错的默认策略通常是:对日常业务记录(订单、工单、项目)使用软删除;对真正敏感的数据(令牌、秘密)以及不应保留的内容使用硬删除。\n\n策略明确后,构建能强制执行它们的流程:用于恢复的“回收站”视图、在审查后进行不可逆删除的“清理队列”、以及展示谁在何时做了何事的审计视图。把“清理”设为比“删除”更难触发,以免被误用。\n\n如果在 AppMaster(appmaster.io)中实现,建议在 Data Designer 中建模软删除字段,并把删除、恢复与清理逻辑集中到一个 Business Process,这样所有屏幕和 API 端点都会应用相同规则。
常见问题
硬删除会把行从数据库中物理移除,之后的查询找不到它。软删除则保留行,但把它标记为已删除(通常用 deleted_at),应用在常规界面中将其隐藏,同时保留历史以便支持、审计和报表使用。
默认对那些以后可能需要解释的业务记录(如订单、发票、工单、消息和账户活动)使用软删除。它能减少意外数据丢失、保持关联完整,并允许在不从备份恢复的情况下进行安全的“撤销”。
当保留数据会带来隐私或安全风险,或保留规则要求真正删除时,硬删除是更合适的选择。常见例子包括密码重置令牌、一次性验证码、会话、API 令牌以及经过验证请求后必须擦除的个人数据。
deleted_at 时间戳是常见选择,因为它既表明记录已删除,又记录了删除时间。它便于实现保留窗口(例如 30 天后清理)并回答审计问题(“何时删除的?”),而无需额外日志来记录时间。
像邮箱或用户名这样的唯一字段在软删除场景下常会阻止重新注册。常见解决方案是让唯一性仅对未删除的行生效,或在删除时改写该字段(例如追加随机后缀)。具体取决于隐私与审计需求。
如果硬删除父记录,可能会使子记录成为孤儿(例如订单),或触发级联删除而抹去大量数据。软删除通常不会改动外键,因此键仍然有效,但你仍需一致地过滤已删除父记录,避免它们出现在下拉列表或用户可见的关联中。
若你硬删除历史行,过去的汇总会发生变化,趋势线会出现缺口,财务数据可能与之前看到的不一致。软删除可以保留历史,但前提是报表与分析查询必须明确是否包含已删除行并一致执行该规则。
“软删除”通常不能满足 GDPR 等法规中的擦除要求,因为个人数据仍可能存在于数据库或备份中。常见做法是立即撤销访问权限,然后对个人标识字段进行不可逆匿名化或在等待期后执行硬删除,同时保留为会计或争议处理所必需的非个人交易事实。
恢复应能把记录带回到一个安全且有效的状态,同时不应恢复那些应当继续删除的敏感项(例如会话或重置令牌)。恢复流程还需要处理关联数据的规则,避免恢复账户后缺失必要的关系或权限。
把删除、恢复与清理行为集中起来,以便所有 API、界面、导出与后台任务都应用同样的过滤规则。在 AppMaster(appmaster.io)中,通常会在 Data Designer 中添加软删除字段,并在一个 Business Process 中实现该逻辑,避免新端点意外暴露已删除数据。


