一物一码验真的并发链路设计:码段管理、四级关联与异常判定

2 阅读5分钟

促销期间扫码峰值能到日常的三十倍。消费者扫码只等两秒,这两秒里系统要做完码有效性校验、首次扫描判定、流向比对、异常规则匹配,还要把结果写进存证链。本文拆解这条链路的存储模型、缓存策略与异常判定实现。

一、架构分层

系统分五层:码段服务负责编码生成与状态管理;赋码服务对接产线设备,完成读码与父子关联;验真服务承接消费者扫码,是唯一的对外读接口;存证服务异步写入链上;分析服务做异常聚合与终端动销统计。

验真服务是整条链路的性能瓶颈,所有设计都围绕它展开:读走缓存,写异步化,规则判定在内存中完成。

层关键组件性能目标
码段服务编码生成、码段状态机批量生成百万级
赋码服务读码器对接、关联写入单线 60 瓶/分钟
验真服务缓存、规则引擎P99 < 300ms
存证服务批量上链、Merkle 根异步,允许分钟级
分析服务聚合、异常检测小时级

二、码段生成与防碰撞

编码要满足三个条件:不可预测、可校验、可批量。

不可预测意味着不能用自增 ID 直接转码,否则造假者拿到一个码就能推算出一整段。常见做法是随机数加校验位:随机段保证不可枚举,校验位让离线设备也能快速识别无效码,避免所有请求都打到数据库。

import secrets, base32

ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"  # 去掉易混淆字符

def luhn_mod36(code: str) -> str:
    total = 0
    for i, ch in enumerate(reversed(code)):
        v = ALPHABET.index(ch)
        if i % 2 == 0:
            v *= 2
            if v >= 36:
                v -= 35
        total += v
    return ALPHABET[total % 36]

def gen_code(length: int = 12) -> str:
    body = "".join(secrets.choice(ALPHABET) for _ in range(length - 1))
    return body + luhn_mod36(body)

码段管理要防止超发和泄漏。码段按批次生成,状态分"已生成、已下发、已激活、已作废"四态,任何状态变更写审计日志。下发到印刷厂或产线的码段只包含加密后的映射,明文码不在外网传输。

三、四级关联与赋码校验

瓶、盒、箱、垛之间是父子关系。存储上用一张关联表即可,不要试图在商品表里加四个字段——层级随品类变化,硬编码会锁死扩展性。

CREATE TABLE code_relation (
  parent_code  VARCHAR(24) NOT NULL,
  child_code   VARCHAR(24) NOT NULL,
  level        TINYINT NOT NULL,      -- 1 瓶-盒 2 盒-箱 3 箱-垛
  line_id      INT,
  created_at   TIMESTAMP,
  PRIMARY KEY (parent_code, child_code),
  KEY idx_child (child_code)
);

产线上必须做读码校验:父码写入前,校验其下所有子码均已成功读取,缺一个就报警剔除。漏读是最常见的现场问题,读码器角度、反光材质、传送带速度都会影响识别率,光靠事后补录会让关联关系断裂。

四、验真服务的高并发读

验真接口的访问模式是典型的"读多写极少":一个码通常只被扫一到三次,但每次都要查库存状态、流向、历史扫描记录。

缓存分两级。第一级用布隆过滤器挡住不存在的码,伪造码在内存里就被判掉,不打到存储。第二级用 Redis 缓存码状态与归属区域,键为码值哈希,值为紧凑 JSON,集中过期时间打散避免雪崩。

写路径异步化:扫码记录先进消息队列,由消费者批量落库并更新统计。验真接口只做"读缓存 + 内存规则判定 + 投递消息"三件事,事务边界尽可能短。

五、存证与链上写入

区块链不适合承载高并发写。工程上的做法是批量打包:每五分钟把这一批流转记录算出 Merkle 根,只把根哈希和批次元数据上链,原始记录留在关系库,靠根哈希做完整性验证。

这样单链写入的吞吐压力被摊平到批次级别,验证时也只需要按记录路径算一遍哈希,成本可控。

六、异常判定规则

异常不是靠模型,靠可解释的规则组合。四类规则覆盖了绝大多数场景:

  • 异地:扫码区域与该码备案流向不一致
  • 高频:同一码在窗口期内被扫次数超过阈值
  • 集中:同一终端短时间内扫到大量不同码,疑似批量回收
  • 跨区:同一经销商关联的多个箱码在不同区域被扫

规则命中后分级:单条异常进线索池,多条同源异常自动升级为工单,绑定处理人和时限。规则阈值必须可配置,不同品类的流通半径差异很大,硬编码的阈值上线三个月就会失真。

七、存储与分区

扫码流水是增长最快的一张表,按时间做范围分区,按月归档冷数据。码主表按码值哈希做分片,避免单表过大导致索引深度增加。

终端动销统计不要在流水表上做实时聚合,用小时级预聚合表,查询走预聚合结果。真需要明细的时候再回查分区表。

八、监控与容量

四个必看指标:验真接口 P99、缓存命中率、消息队列积压、上链批次延迟。

缓存命中率掉到 90% 以下,通常是新批次码未预热;队列积压意味着下游写入扛不住,此时应降级为只读判定、暂缓统计更新,保证扫码不超时。

大促前按历史峰值的三倍做容量评估,重点是缓存容量与连接池,而不是数据库扩容——这条链路里数据库不是瓶颈。

小结

这套系统的技术难点不在区块链,而在三处工程细节:编码不可枚举、关联关系不能断、验真读路径必须缓存化。三件事做扎实,扫码响应速度和稽查效率才成立。