4. 追溯接口返回空数据的链路诊断

问题现象与诊断思路

追溯查询接口对某个已知存在的批次(例如 LOT-20240518-A)返回 {"code":0,"data":null},但该批次在赋码系统中确实已生成。空数据不等于数据不存在,它可能来自四个环节:请求参数被改写、网关路由到错误实例、缓存命中空值、数据库可见性过滤。诊断的核心是沿链路逐层"留痕",而不是直接查库。

查询客户端 lotCode= LOT-20240518-A API 网关 路由规则 /trace/query → trace-svc:v2 检查:参数透传/租户头 追溯业务服务 ① 参数校验 ② 缓存查询 ③ 数据库回源 ④ 可见性过滤 tenantId / status 条件 Redis 缓存 key: trace:lot:… 可能命中空值缓存 追溯数据库 trace_lot 表 tenant_id / status 检查:行是否存在 返回 data:null —— 需逐层定位断点 逐层验证顺序 1. 抓包确认 lotCode 未被 URL 编码/大小写改写 2. 网关日志确认路由实例与租户头 3. 缓存 key 是否命中空值(防穿透) 4. 直连库查行 + 可见性条件

逐层验证的关键动作

第一层:请求参数。在客户端与网关之间抓包,确认 lotCode 原样到达。常见问题是前端把批次号做了 trim 后仍带不可见字符,或网关对 query 做了大小写归一化,导致 lot-20240518-a 与库中大写值不匹配。

第二层:网关路由。查看网关访问日志中的 upstream_addr 与 X-Tenant-Id。若请求被路由到旧版本实例(如 trace-svc:v1),而该实例连接的是历史库,就会出现"数据存在但查不到"。

第三层:缓存。业务服务若对空结果做了短 TTL 缓存(防穿透),则首次查询失败后,后续请求会持续返回空值。用 redis-cli GET trace:lot:LOT-20240518-A 确认是否存在空值占位。

第四层:数据库可见性。直连库执行:

SELECT lot_code, tenant_id, status, deleted
FROM trace_lot
WHERE lot_code = 'LOT-20240518-A';

若行存在但 tenant_id 与请求头不一致,或 status='VOID'、deleted=1,则说明是可见性过滤导致空结果,而非数据丢失。

定位技巧:在业务服务入口打印 lotCode + tenantId + 路由实例IP 三元组日志,一次请求即可判断断点落在参数、路由还是过滤层。

诊断记录表(可直接填写)

层级验证对象期望值实际值结论
参数网关收到的 lotCodeLOT-20240518-A待填一致/被改写
路由upstream_addrtrace-svc:v2 实例待填正确/错路由
缓存trace:lot:key无 key 或非空值待填空值缓存/正常
数据库trace_lot 行存在且 tenant 匹配待填可见/被过滤

按表逐行填写后,第一个"实际值≠期望值"的层级即为断点位置。若四层均正常而接口仍返回空,则需检查业务服务的序列化逻辑是否把非空对象转成了 null。