2. 追溯码重复赋码冲突的根因分析与处置
问题定位:同一追溯码为何落到两个产品单元
追溯码重复赋码的本质是“一码一物”约束被破坏。排查时不要先猜原因,而应把三类证据按时间轴对齐:赋码流水(谁在什么时间申请/写入了该码)、设备日志(喷码/贴标设备是否重发、重试、离线补传)、数据库唯一约束记录(唯一索引冲突报错、事务回滚、幂等键命中情况)。三者交叉后,通常可归入设备重发、人工补码、系统并发三类根因之一。
用赋码流水与设备日志区分三类根因
先取该追溯码在赋码流水表中的全部记录,按 created_at 升序排列,重点看四个字段:operator_id、device_id、work_order、write_result。再取设备日志中同一码的喷码指令记录,比对 cmd_seq(指令序号)与 retry_count(重发次数)。判断规则如下:
- 设备重发:两条流水来自同一
device_id,时间差在毫秒级,设备日志中retry_count ≥ 1,且无人工补码单。说明设备在未收到确认时自动重发,而服务端未做幂等。 - 人工补码:两条流水
operator_id不同,时间差通常为分钟到小时级,且存在补码申请单。说明补码时未校验原码是否已绑定有效产品单元。 - 系统并发:两条流水时间差极小(如 < 10ms),来自不同服务实例或线程,数据库唯一约束记录中出现重复键冲突但被吞掉,或唯一索引根本未建立。说明“先查后写”存在竞态窗口。
数据库唯一约束记录的关键读法
唯一约束记录是判定并发问题的直接证据。检查 trace_code_binding 表是否在 trace_code 上建立了唯一索引;若已建立,冲突时应抛出重复键错误(如 MySQL 的 1062),并可在应用日志中看到对应事务回滚。若日志中只有“查询无记录→插入成功”两条记录而无冲突报错,则说明唯一索引缺失或写入走了不同分片,属于结构性缺陷。
-- 检查唯一索引是否存在
SHOW INDEX FROM trace_code_binding WHERE Column_name = 'trace_code';
-- 定位重复绑定的两条记录
SELECT id, trace_code, product_unit_id, operator_id, device_id,
created_at, status
FROM trace_code_binding
WHERE trace_code = 'T20240518001'
ORDER BY created_at;
去重与补偿方案设计
处置原则是“保留首次有效绑定、作废重复绑定、补发新码、回写追溯链、留审计”。具体动作:
- 以最早一条
status='active'的绑定为准,保留其product_unit_id;其余重复绑定置为void,并记录void_reason='duplicate_binding'。 - 对被作废绑定所对应的产品单元,重新申请新追溯码并补打标签,确保实物与系统一致。
- 在赋码服务入口增加幂等键(如
device_id + cmd_seq),并在数据库层强制唯一索引,双保险防止再次并发写入。 - 补码流程增加“原码状态校验”:若原码已绑定有效单元,必须先作废再补码,禁止直接覆盖。
验证处置是否成功:再次查询该追溯码,应只返回一条
active 绑定;被作废记录保留可审计;新补发的码能正常关联到原产品单元且追溯链完整。不要直接物理删除重复绑定记录。追溯数据要求可审计,删除会破坏排障证据链,正确做法是状态置为作废并保留时间戳与操作人。