Skip to content

数据维护:逻辑过期、分批清理与对象回收 ​

验证码超过有效期时应立即拒绝使用,后台清理可以稍后删除记录。用户是否登录、清理任务是否按时运行,都不应改变验证码的有效性。

同样的分工也适用于临时下载链接、过期会话和缓存记录:请求路径判断能否使用,后台任务回收占用的存储。

从到期失效推导后台回收 ​

假设验证码有效 5 分钟,开发者可能先想到“5 分钟后删掉这一行”。删除发生得晚、清理进程停止或记录尚在副本上,都不应让一次业务校验继续接受过期凭据。

候选方案能完成什么需要补充的条件
仅靠延迟删除或 TTL回收部分过期存储不能把物理存在当作仍有效
仅在请求内检查期限正确拒绝过期使用长期残留仍占用存储
请求内校验加后台分批回收分别满足有效性与容量要求保留策略、清理范围与删除恢复

本篇选择第三种。逻辑有效期由业务操作判断,清理期限由保留策略判断。审计记录即使不再可用,也可能仍需要保留;业务过期不自动意味着允许马上物理删除。

逻辑校验与物理回收时序 ​

图中前一部分是验证码业务校验,后一部分是临时对象回收,分别说明数据库内原子操作和跨系统删除的边界。

业务失效不必等待物理删除;外部删除不确定时则要核对原目标,不能扩大清理范围。数据库内到期记录的分批删除见后文 SQL。

维护需求与数据保留边界 ​

示例把验证码逻辑有效期设为 5 分钟,后台按自己的清理周期回收。5 分钟是业务规则样例;审计记录、文件留存和用户删除请求分别定义保留期限与完成承诺。

需求示例决策实现约束
到期是否还能使用不能,独立于清理进度请求路径比较期限和撤销状态
哪些记录可删除到期、无活跃引用、符合保留策略候选查询与删除前复核
删除失败怎么办记录目标,按对象独立重试不整批重新扩大范围
保留例外怎么办审计、冻结或活跃会话显式排除有版本的保留策略与 hold 标记
谁能手动清理受限的维护身份目标范围、操作原因和结果可审计

创建记录时保存期限 ​

数据业务侧判断后台回收的对象
验证码已使用或当前时间达到期限即不可用符合保留策略的过期挑战记录
临时文件按下载授权、引用状态和有效期判断能否访问无引用的文件或对象版本
会话按有效期、撤销状态和安全策略判断能否继续使用已失效会话记录
任务历史已完成且超过保留窗口才可回收归档后的实例与尝试记录

验证码校验还需要绑定用户或业务上下文,控制尝试次数,并用原子操作避免重复使用。只依靠清理线程删掉旧记录,会让清理延迟变成有效期延长。

MongoDB 官方说明 TTL 删除是后台进行的,到期记录不保证立即消失。TTL 可以回收空间,授权与有效性判断仍要自己完成。

数据结构与使用接口 ​

记录关键字段与约束
verification_challenges用户、用途、摘要、有效期、剩余尝试、使用时间
temporary_objects对象版本、所有者、引用计数或关系、期限、hold、回收状态
cleanup_runs策略版本、过滤范围、截止水位、检查点、扫描与成功数量
cleanup_items本轮目标身份、对象版本、删除结果、失败原因与尝试

签发接口返回挑战标识,不回传可复用的服务器凭据;校验接口绑定当前用户和业务用途。临时下载接口校验权限与期限,物理对象仍存在不代表允许继续访问。

维护操作可以分为候选预览、创建清理运行和查询进度。预览之后对象可能被重新引用,因此正式执行仍要在事务中复核,不能直接删除整份旧预览列表。

验证码的一次性消费 ​

下面的设计片段假定请求已经通过认证、用途绑定与频率控制,摘要由服务端根据本次输入计算。它只展示“未使用且未过期”的原子消费条件:

sql
UPDATE verification_challenges
SET used_at = statement_timestamp()
WHERE id = :challenge_id
  AND user_id = :user_id
  AND purpose = :purpose
  AND code_digest = :computed_digest
  AND used_at IS NULL
  AND attempts_remaining > 0
  AND expires_at > statement_timestamp()
RETURNING id;

错误输入还要原子扣减尝试额度并限流;成功消费与需要完成的本地业务尽可能同事务提交。生成、保存和校验还需满足随机性、安全存储、单次使用等要求,可参考 OWASP 的一次性令牌建议。上面片段不涵盖完整凭据保护流程,不能把摘要放进访问日志或错误响应。

并发消费时只有符合条件的更新能够成功。清理线程不参与有效性判断,暂停清理也不会扩大 5 分钟窗口。

used_at IS NULL 保护单次使用,expires_at 保护时间边界,user_id 与 purpose 保护使用上下文。只保留“摘要相同”这一个条件,会丢掉这些不同约束。条件更新成功后,受保护的本地业务仍需在同一事务完成,才能避免验证码消耗了、业务却没有提交。

1000 条记录同时过期 ​

为到期字段建立合适索引,每次领取一小批并提交。下面是 PostgreSQL 18 的清理语句设计示例,展示每批最多 100 条;它没有在真实数据库执行。

sql
WITH expired AS (
    SELECT id
    FROM verification_challenges
    WHERE expires_at <= statement_timestamp()
    ORDER BY expires_at, id
    LIMIT 100
    FOR UPDATE SKIP LOCKED
)
DELETE FROM verification_challenges AS v
USING expired AS e
WHERE v.id = e.id
  AND v.expires_at <= statement_timestamp()
RETURNING v.id;

1000 条约需 10 批,但批次之间可以持续推进,不必每处理 100 条就再等一轮很长的定时周期。根据在线负载设置每轮最大耗时、总批数和退避。

实际删除还需要检查外键、审计和数据保留要求。数据库行被删除后,磁盘空间如何回收取决于存储引擎维护机制,不能直接按删掉的行数计算立即释放的磁盘。

数据库清理的运行边界 ​

短事务删除适用于能够直接删除的记录;需要外部操作的对象先保存 cleanup_item,再交给独立 Worker。不要在数据库事务里等待对象存储删除。

每轮记录策略版本、截止水位和实际处理数量。重试可以按精确目标继续,也可以安全重扫剩余候选,但不能因为一次失败改为清理更宽的用户或路径范围。

并发清理器使用领取锁或条件更新协调。正在续期的记录重新满足保留条件时,删除语句需要重新检查,避免使用过期候选快照。

临时文件需要跨系统回收 ​

文件元数据与对象存储通常不在同一事务中,可以采用标记、删除、确认的流程:

  1. 领取到期候选,检查没有活跃引用或上传会话。
  2. 条件标记待删除,保存对象键和版本,阻止新的正常引用;需要恢复时走明确的状态转换。
  3. 事务外删除确切的对象版本,按存储接口语义处理“已不存在”。
  4. 保存删除结果,失败时仅重试对应对象。

删除前必须防止对象键复用。领取时看到的旧临时文件,不应因为相同路径被新文件替换而误删新内容。

这里描述的是清理系统设计,不是让扫描任务任意按宽泛路径删除文件。清理目标应来自受控业务记录和精确对象身份。

生命周期策略的边界 ​

对象存储的生命周期规则可以自动回收部分临时对象,但要核对版本与删除标记的语义。例如 S3 对象过期在不同版本控制状态下行为不同,当前版本过期不等于所有历史版本都已物理删除。

业务已经删除而对象仍在、对象已删除而元数据未确认,都需要可重试的核对流程。单次调用超时不能确定删除究竟有没有完成。

不要把去重依据一起删掉 ​

任务历史到期后,如果同时删掉唯一业务键,历史事件重放时可能再次创建同一通知或补偿动作。保留期限要覆盖恢复与重放窗口,必要时把长期幂等记录从任务实例中分离。

监控最老待清理记录年龄、每批耗时、失败目标和在线数据库压力。大多数清理失败需要通知运维负责人,而不是逐条推送给用户;实际用户数据删除进度则按产品约定单独展示。

对象回收 Worker ​

text
领取 cleanup_item,核对本轮策略与精确对象版本
BEGIN
  锁定元数据,检查 hold、引用、期限和当前回收状态
  如果重新被使用:退出清理或进入明确的恢复流程
  标记 deleting,阻止未协调的新引用,保存外部删除意图
COMMIT
事务外按确切版本提交删除
确认已删除:条件更新元数据与 item 为完成
返回暂时失败或结果不确定:保留待核对状态后重试或查询

创建新引用的业务路径必须遵守同一 deleting 状态约束,否则清理器的标记无法保护对象。删除请求已经在途时,也不能简单清掉 deleting 标志就认为对象恢复可用。

一次验证码失效与对象回收的推演 ​

操作或时间逻辑判断后台结果
验证码 V7 在 10:00 签发,10:05 到期到期前按用途与使用状态校验清理可以尚未启动
10:05:01 用户提交 V7期限条件不成立,拒绝记录物理存在不改变结果
10:10 清理恢复对符合保留策略的 V7 执行回收不回头修改先前的校验结论
临时对象 X2 被列为待清理后重新引用正式领取时发现有效引用,跳过预览列表不能直接授权删除
旧键后来写入新对象版本清理仍绑定原来的精确版本新内容不能被旧任务顺带删除

数据库到期行的删除,与外部对象的两阶段回收,具有不同恢复窗口。看到两者都叫“清理任务”时,应先问状态能否与副作用同事务提交,再决定使用一条删除语句还是独立的删除意图与核对流程。

验收与排查依据 ​

场景应断言的结果
清理停机一小时过期验证码仍被拒绝使用
同一验证码被两个请求同时消费只有一次有效消费
候选预览后文件被重新引用正式领取时复核,阻止误删
相同对象键写入新版本清理只能命中已领取的旧版本
删除成功但确认响应丢失查询或幂等重试原目标,不扩张删除范围
历史任务归档后事件重放长期业务去重依据仍阻止重复副作用

区分逻辑失效延迟与物理回收延迟:前者应满足业务有效期,后者按存储与维护预算约束。记录跳过原因、删除失败年龄与受影响目标数量。

阿里云 OSS 生命周期与业务回收的分工 ​

阿里云 OSS 的生命周期概述介绍了按前缀、标签等条件筛选对象后进行转储、过期删除或清理未完成分片。规则加载和周期扫描需要时间,因此它适合存储维护,不能用来实现验证码到秒失效或即时撤销下载权限。

本文建议先由业务标记对象的回收资格,再对用途与保留期限明确的临时目录使用生命周期规则。订单附件、取证文件与普通缓存不应仅因位于同一 Bucket 就使用相同删除规则;文档也说明,对象保留期会影响删除。1000 个文件到期时,业务访问层先按期限拒绝访问,物理回收可以随后分批完成。

配置本身也有故障窗口:官方说明通过 API、SDK 或 ossutil 调用 PutBucketLifecycle 会覆盖整个 Bucket 的生命周期配置。自动化维护规则时,应读取完整配置、合并修改并校验差异,再提交全部规则;这与逐个追加一条规则的直觉不同。本文仍需单独保留任务去重依据和删除结果记录,不能把存储生命周期当成业务审计账本。

火山引擎 TOS:清理当前对象、历史版本和上传碎片 ​

火山引擎技术服务的 TOS 生命周期清理案例以清空大量对象的存储桶为例,讨论当前对象、先前版本和未完成分片的处理。它说明维护对象可能不止文件列表中看到的当前对象;当前文件不可见也不能直接证明历史数据与碎片都已回收。

应用到本文时,可以把业务临时文件、历史版本和中断上传分别统计。回收目标记录精确版本或其他可验证的对象身份,核对本轮实际释放结果。该案例的全桶范围不适用于混有正式附件的业务桶;这里只借用清理对象分类,示例保留期限仍由业务自行确定。

美团 MySQL 巡检:检测结果需要进入治理闭环 ​

美团的 MySQL 巡检系统实践介绍使用 Crane 调度巡检,执行机异常时可重新执行,并要求结果幂等入库;还将处理方案、负责人催办与治理周期纳入系统。这使“任务执行完成”和“发现的风险已经消除”成为可分别跟踪的结果。

本文的数据维护可以借鉴这一点:清理任务发现某文件被保留策略阻止,应记录原因和责任方,后续依据策略变化重试或人工处理,而不是每晚重复报一个笼统失败。涉及大表或大量对象时,还要记录清理速率、剩余量和对在线业务的影响。巡检发现只读风险与执行不可逆删除是不同权限等级,不能因同由调度器触发就合并授权。

参考资料 ​