-
perf(矩阵): 潜在治疗标签预计算落库 —— 958ms → 110ms,24 格逐字未变 · 1293108a
初选矩阵此前每次开页面都从最原始的事实现推: plan_reasons → 展开 evidence.factIds → 回查 patient_facts(1567 万行 / 18 GB) 最忙的诊所(15,884 条 plan)一次摊 34,459 次随机查、372 MB I/O —— **而这一切的唯一产出就是一个标签字符串**。温度那根轴根本不碰 fact (它来自 patient_profiles.last_visit_at)。 ⇒ 加一列 `plan_reasons.potential_labels text[]`,矩阵改成 followup_plans ⋈ plan_reasons ⋈ patient_profiles + unnest。 ■ 实测(本地,15,884 条 plan 的诊所,与测试服 15,770 同规模) 耗时 958 ms → 110 ms (8.7×) 磁盘读 119,861 → 13,500 (-89%) JIT 触发 → 不触发 小诊所 11 ms → 0.4 ms (地板消失)🔴 **五个诊所的 24 格逐字相同**(scripts/bench-matrix.ts 的结果快照 diff 零差异)—— 性能改动最容易的失败是悄悄少算一批人,只比耗时看不出来。 ■ 为什么是数组而不是单列 一条依据可挂多个 fact:93.7% 只有一个,但**3.7%(本地 1,391 条)会推出不止一个 code**。⛔ 存单值会把这批人算少。 ■⚠ ️ 年龄被冻结,所以必须刷 规则里三条带年龄(K08>18 / K07 3~12 / K07 13~40)⇒ 标签不是纯函数。 实测受影响 5,551 人中「明天跨档 0 人、30 天内 16 人」(≈0.5 人/天,占矩阵 1.4 万人的 0.003%)。 ⇒ 每晚 03:30 全量重算(PlanLabelRefreshService);⛔ 不刷就逐日漂,且不会有任何报错。 ■ 三条写入路径共用同一份 SQL🔴 ⛔ 绝不在写入侧用 TS 再算一遍:规则表是产品配置,第二份实现迟早和矩阵那份漂开, 而漂了之后矩阵的数会**静默**不一样。 · 生成完 plan 立刻 backfillMissing() ——⛔ 不补的话新 plan 的列是 NULL, `unnest` 直接跳过 ⇒ **今天生成的人在矩阵里看不见**,且不报错。失败不阻断生成。 · 每晚全量重算(年龄) · 部分索引 `WHERE potential_labels IS NULL` —— 平时几乎是空的,补齐即 O(1), 否则每次生成完都要为找 NULL 扫一遍 31 万行。 ■ 两个自己踩了又修的坑(都写进了注释与测试) · 全量重算最初只写 `ORDER BY id LIMIT n` **没有游标** —— 每次取同一批,原地打转, 而且不报错只是每晚白跑。改成按 maxId 推进。 · 停止条件最初看"写了几行" —— 全量重算时绝大多数行算出来跟原值一样、不会被写, written=0⛔ 不等于做完了。改成看 seen / maxId,并把两者分开报。 列**刻意可空**,三态有别:NULL=没算过 / '{}'=算过但推不出标签 / 非空=标签集。⛔ 别加 NOT NULL DEFAULT '{}' —— 那样刷新任务再也找不到漏网的行。 验证:tsc 通过;jest 86 套 1342 例全过(新增 12);eslint 干净; 真 AppModule 起容器确认两个 service 都注入成功; 回填 37,300 条/8.4s;全量重算 37,300 条/2.7s 未卡死;人为挖 25 个洞→补齐→剩余 0。luoqi committed
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| scenarios | Loading commit data... | |
| chain-composer.service.ts | Loading commit data... | |
| plan-engine.service.ts | Loading commit data... | |
| priority-scorer.ts | Loading commit data... | |
| reason-refresh.ts | Loading commit data... | |
| scenario.interface.ts | Loading commit data... |