Commit dc760962 by luoqi

docs(gap): 生产实测集合式全量更慢,已回退 —— 子集掩盖了性能的规模拐点

生产 10 万患者子集对拍 11/11 零差异 → 开启 → 20:15 轮第 1 批判据失败 → 回退。
  missing_tooth 8m13s → 30m42s(慢 3.7×)
  endo_no_rct   8m12s → 18m21s(慢 2.2×)
  第 1 批墙钟   17m03s → 30m42s(+80%)
命中数仍在漂移带内 —— **正确性没问题,是性能不达标**。
临时文件零增长、swap 为 0 —— 也不是内存问题(这次没再编错误归因)。

归因:集合式代价跟**患者域**走(gap_scope 要预聚合全域),legacy 跟**候选行数**走。
规模一变结论就翻转:30K/585K/10万子集 都快 1.5×,113 万全量慢 3.7×。

 教训:方案里写过「子集零差异 ≠ 全量零差异」,但只想着正确性覆盖。
   真正被掩盖的是**性能**,而且方向都反了。子集测正确性有效,测性能无效 ——
   除非能先证明代价与规模线性,而集合式恰恰不是。

生产维持 legacy + 并发4(场景段 50m25s / 整轮 1h17m)。代码留 main,默认关闭。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent aa23e1c8
......@@ -484,3 +484,69 @@ deploy-prod.sh 的文件头注释本来就写着"不信 compose 的 diff 启发
上生产前应先跑**只读对拍**(`verify-gap-equivalence --host=jvs-dw`,不写库、不改配置),
确认生产数据分布上同样零差异 —— 生产与测试机的数据快照不是同一份。
---
## 15. 🔴 生产实测:集合式在**全量规模上更慢**,已回退(2026-08-30 21:32)
**结论:集合式正确但不可用。生产保持 legacy + 并发4。**
### 时间线
| 时刻 | 动作 |
|---|---|
| 18:21~19:14 | 生产 **10 万患者子集**对拍:11/11 **零差异**,行数逐个相同 |
| 19:17 | 按门开启 `PAC_GAP_VARIANT=setbased` |
| 21:07~21:30 | 20:15 轮第 1 批出结果 → **判据失败** |
| 21:32 | 回退,生产恢复 legacy |
### 数据
| 子场景 | legacy(16:15) | setbased(20:15) | |
|---|---|---|---|
| missing_tooth | 8m13s | **30m42s** | **慢 3.7×** |
| endo_no_rct | 8m12s | 18m21s | 慢 2.2× |
| ortho_no_consult *(全口,两版同一条 SQL)* | 8m14s | 8m24s | *噪音* |
| perio_no_srp *(全口,两版同一条 SQL)* | 17m03s | 15m23s | *噪音* |
| **第 1 批墙钟** | **17m03s** | **30m42s** | **+80%** |
命中数全部仍在漂移带内(missing_tooth 79,874 / endo 72,640)——**正确性没有问题**
### 归因:集合式的代价跟「患者域」走,legacy 跟「候选行数」走
- `gap_scope` 要把**全域患者**的 resolved 牙位预聚合一遍;域越大,这份固定成本越贵
- legacy 的逐行相关子查询只在**候选行**上付费
所以规模一变,结论就翻转:
| 患者域 | missing_tooth |
|---|---|
| 本地 30K | 集合式快 1.46× |
| 测试机 585K | 集合式快 1.46×(相邻两轮 C/D) |
| 生产 10 万子集 | 集合式快 1.53× |
| **生产 113 万全量** | **集合式慢 3.7×** |
### ⛔ 教训:子集验证掩盖的不只是覆盖,还有**性能的规模拐点**
方案里写过「子集零差异 ≠ 全量零差异」,但那句话只想着**正确性覆盖**
真正被子集掩盖的是**性能**:集合式在 10 万上快 1.53×、在 113 万上慢 3.7×,
**方向都反了**
- 子集测正确性:有效(差异一旦出现就是真差异)
- 子集测性能:**无效**,除非能证明代价与规模线性 —— 集合式恰恰不是
- 测试机 585K 的收益也不能外推到生产 113 万,**两者隔着这个拐点**
### 保留了什么
- 代码留在 main,默认 legacy,不产生任何行为
- 对拍工具(`verify-gap-equivalence`)是本轮最有价值的产出,与形态无关:
`--self` / `--persona` / `--single` / `--conc` / `--subset` 五种模式,
后续任何 gap 口径改动都该先过它
- 集合式若要复活,前提是解决「代价随患者域增长」这一条 ——
例如按诊所/批次切分 scope,让每次预聚合只覆盖一小片
### 现状
生产 = **legacy + `PAC_RECALL_SUBSCENARIO_CONCURRENCY=4`**,场景段 50m25s、整轮 1h17m。
2 小时窗口的余量仍然偏紧(今日 14:15、18:15 两次跳轮),下一步应看**摄入 34 分钟**
**plan 预取 20 分钟**,而不是继续在场景段上使劲。
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment