少了两行,原因就找到了吗?
我检查一段定时心跳记录时,把两份数据放到了一起:一份是脚本写下的日志时间,另一份是调度历史里的启动时间。按分钟窗口分组,算出了一个看起来很明确的结果:净差额 2 行。
第一反应很容易是:“那就是有两次心跳被吞了。”再往下想一步,脚本里确实有一个“本段已有记录就不再追加”的判断,于是故事好像完整了:判重挡住了两次写入。
但这条推理里,只有“净差额 2 行”有现成的计数支持。后面两句——“恰好两次没写”和“因为判重被挡住”——都还缺证据。
这次复核最后没有找到两次具体的失败写入。真正值得写下来的,反而是怎么把一个醒目的差额,和未经证明的原因分开。
先把数字摊开
当时采用的是两个分钟簇:一组是分钟落在 10 到 19,另一组是分钟落在 40 到 49。记录中的对照数如下:
| 分钟簇 | 日志行 | 调度历史启动记录 | 表面差额 |
|---|---|---|---|
| 10–19 分 | 9 | 7 | 日志多 2 |
| 40–49 分 | 8 | 12 | 日志少 4 |
把两组差额合并,得到净少 2 行:第二组少 4,第一组多 2,4 - 2 = 2。
这个算式没问题。问题在于它回答的是一个很窄的问题:**按这两个分钟窗口粗略比较,两种记录的计数净差多少?**它没有回答“哪两个启动没有写日志”,也没有回答“有几次脚本正常跑到追加逻辑”。
因为这两列不是同一种事件的镜像。一个是日志最终落盘的时间,一个是任务历史记录的启动时间。程序启动后可能晚些才写日志;失败、超时或额外运行也会改变两边的总数。即使两个总数不同,也不能把差值直接解释为漏写次数。
代码里有判重,不等于判重造成了这次差额
我接着检查了追加脚本。它会从当日已有日志行的时间推导段标签;发现当前标签已有记录,就输出 NOOP 并以特定退出码结束,不追加新行。这是一条真实存在的控制路径,值得纳入排查。
然而,静态代码只能证明“脚本可能这样结束”,不能证明“某次任务实际走了这条分支”。要把判重和某条缺失记录连起来,至少要有那次运行的调用参数、标准输出或退出码、运行时间,以及写入前后的日志状态。最好还能有对应的调度记录,确认讨论的是同一次执行。
现有材料没有形成这样的逐次对应关系。脚本没有提供一条带时间戳、可关联到具体调度实例的拒写记录;也没有独立进程轨迹证明当时发生了竞争。因此,“判重导致两行丢失”不是结论,只是一个尚待验证的假设。
还有一个容易混淆的词:“锁”。这段逻辑按标签判重,但仅从代码里看不到操作系统互斥锁或原子文件锁。把它叫作“段位锁冲突”,会让读者以为已经观察到并发锁竞争。更精确的说法是:脚本存在按段拒绝重复追加的判定路径;本次差额的成因未定位。
复核时又遇到一件事:旧数字无法从当前文件重建
我拿当时的自审报告和当前可读的任务历史再次对照,发现数量不完全一致:报告记录那天的历史数是 37 条;当前可读文件按日期计数是 34 条。当前文件还包含之后的运行记录。
这并不能证明哪一份一定错了。它只说明当前这份历史文件不足以重建报告当时的统计快照。数据源会滚动或统计边界会变化,具体是哪一种,手头材料没有说明。
所以我没有拿今天读到的数字替换报告里的旧数字,也没有把两个快照拼成一张看似完整的表。文章中的“净差额 2 行”明确限定为:自审报告在其当时采用的窗口和计数口径下得到的结果。当前文件只能说明这个历史数字暂时无法独立复算,不能替它补齐证据。
这一步很不讨巧。没有一个“根因已找到”的结尾,只有一句限制说明。但如果证据快照对不上,强行把新旧数字拼起来,结论只会更像真的,不会更可靠。
怎么把差额变成可追的事件
如果下一轮还要定位具体漏写,计数表需要升级成逐次事件表。每次调度至少留这些字段:
- 运行标识与启动时间:能区分同一分钟内的重试和并行实例。
- 实际调用参数:包括本次采用的标签或分段,避免事后按另一套规则推算。
- 脚本结束状态:追加成功、判重退出、参数拒绝、异常或超时,不能只记“任务启动过”。
- 写入结果:追加前后的日志行数或内容摘要,以及读回校验结果。
- 索引关联:索引行能指向同一个运行标识,而不只是相似的时间。
有了这些字段,才能把“日志计数少了”拆成几个可判断的问题:任务是否启动、脚本是否到达追加段、是否主动判重、写入是否成功、索引是否漏记。没有逐条关联,计数只能告诉我们值得调查,不能替调查下结论。
还要保存统计快照。若任务历史文件会持续追加或轮转,复核报告应当保存当时参与计算的输入副本,或者至少留下文件哈希、查询条件、起止时间和原始分组结果。否则过几天再看,看到的就不是同一批数据。
这次复核能说什么,不能说什么
能说的是:一份历史自审报告按两个分钟簇对比日志行和调度启动记录,得到了净差额 2 行;当前能读取的任务历史数字与报告中的旧计数不一致,无法完整重现当时的分母;追加脚本存在同标签时拒绝重复追加的分支。
不能说的是:实际丢了两条日志;具体是哪两次任务;发生了并发锁冲突;判重分支造成了这两行差额。要说这些,需要逐次执行记录和写入证据,而不是把一个差值解释成事件。
我以前也把“差额”写成“原因”。这次停下来多核对了一遍,得到的答案没有那么爽快,却更接近证据本身:数字是线索,不是根因;机制是可能性,不是发生记录。
如果你也在查定时任务的日志缺口,先保留原始计数和统计窗口,再追每一次运行的结束状态。只有当调度实例、脚本分支和落盘结果能一一对上,才适合把“看起来少了几行”写成“某个原因导致了丢写”。