促销期间扫码峰值能到日常的三十倍。消费者扫码只等两秒,这两秒里系统要做完码有效性校验、首次扫描判定、流向比对、异常规则匹配,还要把结果写进存证链。本文拆解这条链路的存储模型、缓存策略与异常判定实现。
一、架构分层
系统分五层:码段服务负责编码生成与状态管理;赋码服务对接产线设备,完成读码与父子关联;验真服务承接消费者扫码,是唯一的对外读接口;存证服务异步写入链上;分析服务做异常聚合与终端动销统计。
验真服务是整条链路的性能瓶颈,所有设计都围绕它展开:读走缓存,写异步化,规则判定在内存中完成。
| 层 | 关键组件 | 性能目标 |
|---|---|---|
| 码段服务 | 编码生成、码段状态机 | 批量生成百万级 |
| 赋码服务 | 读码器对接、关联写入 | 单线 60 瓶/分钟 |
| 验真服务 | 缓存、规则引擎 | P99 < 300ms |
| 存证服务 | 批量上链、Merkle 根 | 异步,允许分钟级 |
| 分析服务 | 聚合、异常检测 | 小时级 |
二、码段生成与防碰撞
编码要满足三个条件:不可预测、可校验、可批量。
不可预测意味着不能用自增 ID 直接转码,否则造假者拿到一个码就能推算出一整段。常见做法是随机数加校验位:随机段保证不可枚举,校验位让离线设备也能快速识别无效码,避免所有请求都打到数据库。
python
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)
码段管理要防止超发和泄漏。码段按批次生成,状态分"已生成、已下发、已激活、已作废"四态,任何状态变更写审计日志。下发到印刷厂或产线的码段只包含加密后的映射,明文码不在外网传输。
三、四级关联与赋码校验
瓶、盒、箱、垛之间是父子关系。存储上用一张关联表即可,不要试图在商品表里加四个字段------层级随品类变化,硬编码会锁死扩展性。
sql
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% 以下,通常是新批次码未预热;队列积压意味着下游写入扛不住,此时应降级为只读判定、暂缓统计更新,保证扫码不超时。
大促前按历史峰值的三倍做容量评估,重点是缓存容量与连接池,而不是数据库扩容------这条链路里数据库不是瓶颈。
小结
这套系统的技术难点不在区块链,而在三处工程细节:编码不可枚举、关联关系不能断、验真读路径必须缓存化。三件事做扎实,扫码响应速度和稽查效率才成立。