开发文档

鲜花交易系统与鲜花交易平台(PHP / ThinkPHP 版)的研发记录:功能上线、架构演进与实施经验,按发布日期持续更新。

用业务号做关联,必须配好索引:一次补齐 142 个引用索引

设计选择带来的必然代价

这套系统的关联方式是"业务号对业务号":订单明细通过 order_biz_no 关联订单,结算单通过 order_biz_no 关联订单,资金流水通过 biz_no 关联业务单。好处很多——跨库、分表、数据复制时不会因自增 ID 变化而断链。

但有一个必然代价:每次关联查询都落在业务号列上。如果这一列没有索引,关联就是全表扫描,而且随着数据量增长会指数级变慢。

自增主键有 MySQL 自动建的索引,业务号没有。这就是那次索引体检的出发点。

体检怎么做

不靠肉眼看 DDL,而是把"引用关系"和"索引现状"放到一起比对:

  1. 从 information_schema.COLUMNS 里取出所有以 _biz_no 结尾的列,得到"可能被用作外键的列"清单;
  2. 从 information_schema.STATISTICS 里取出每张表已有的索引及列顺序;
  3. 求差集:出现在业务表里、但没有任何索引以它为最左列的 *_biz_no 列;
  4. 对高频查询做 EXPLAIN 抽样验证,确认确实走的是全表扫描而不是别的索引。

第 3 步是关键:索引存在但不在最左列,等于没用。idx(status, order_biz_no) 无法支撑只按 order_biz_no 的查询。

结果:补了 142 个索引

按域分布补齐,重点在三个地方:

域典型列说明
供应链order_biz_no、supplier_biz_no、product_biz_no、sku_biz_no订单详情、结算、库存流水的高频关联
资金merchant_biz_no、bill_biz_no、source_no对账与结算查询
系统与分析user_biz_no、ref_biz_no操作日志、埋点与业务单反查

原则是只补引用列,不动已有复合索引。复合索引的列顺序往往服务于特定查询,随意调整会让另一条查询变慢。

唯一约束:哪些列必须加

索引解决"快不快",唯一约束解决"对不对"。业务号列里必须加唯一约束的有两类:

  • 单据主表的 biz_no:订单、结算单、账单、回款,业务号重复意味着同一笔业务被记录了两次,这必须由数据库拦住;
  • 日志类表的 biz_no:即便日志是追加写,业务号重复也说明写入逻辑有 bug,早暴露比晚暴露好。

这次补齐的两张表属于第二类。补之前先做了一次查重,确认存量数据没有重复值——加唯一约束前不查重,等于赌数据是干净的。

索引不是越多越好

补索引的同时也守了两条边界:

  • 不给低基数列建单列索引。status(只有几个值)、is_deleted(0/1)单独建索引意义不大,应该做成复合索引的次列;
  • 不为"可能会查"建索引。每个索引都要占写入成本,只有真实查询场景支撑才建。

这两条在数据量小的时候看不出差别,等单表到百万级,写入放大会非常明显。

沉淀成检查项

这次整改之后,索引检查变成了固定流程:新增表必查引用列是否建索引,新增唯一约束前必查重。数据库的问题不会自己消失,只会等到数据量足够大时集中爆发。

本文属于「开发文档」系列,记录鲜花交易系统(PHP + ThinkPHP 8 + MySQL 8)的实际研发进展。想了解功能清单与报价,可前往系统报价或联系我们。