Commit dc1729fb by luoqi

merge: 场景查询 work_mem=256MB → test

parents 736d95fb 7d20ca2b
Pipeline #3614 failed in 0 seconds
...@@ -517,7 +517,32 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin { ...@@ -517,7 +517,32 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin {
// 2026-08-29 排查时最大的困难,就是知道 plan 段慢、却分不清慢在 SQL 还是慢在 Node, // 2026-08-29 排查时最大的困难,就是知道 plan 段慢、却分不清慢在 SQL 还是慢在 Node,
// 只能靠采样 pg_stat_activity 反推。有了这两个数,看一眼日志就知道该往哪查。 // 只能靠采样 pg_stat_activity 反推。有了这两个数,看一眼日志就知道该往哪查。
const sqlStart = Date.now(); const sqlStart = Date.now();
const rows: HitRow[] = await this.prisma.$queryRaw(scenarioSql); // ⚡ 本查询单独抬高 work_mem —— 2026-08-29 **生产**六轮实测(endo_no_rct,72,769 行):
// 配置 耗时 相对基线
// 无索引 + 4MB (冷) 18:43
// 无索引 + 4MB (暖) 17:33 ← 基线;冷暖只差 6%,缓存不是主因
// 无索引 + 256MB (暖) 11:37 −34%
// 无索引 + 256MB (暖,复现) 12:12 −30% ← 两次复现一致
// 有索引 + 4MB (暖) 16:42 −5% ← 落在噪声里,索引已撤
// 有索引 + 128MB (暖) 14:47 −16% ← 128MB 只吃到一半收益
//
// ⭐ 取 256MB 而不是 128MB:收益对取值**很敏感**,128→256 还差一倍。
// 原本按"溢出只有 4 批、内存用量 8MB"推断 128MB 够用,实测否定了这个推断。
// 原因:默认 4MB 太小,EXPLAIN ANALYZE 里看到哈希溢出成 4 批
// (Buckets:131072 Batches:4 Memory Usage:8032kB)+ 临时文件读写 11,865 页。
//
// ⛔ 必须用事务级 SET LOCAL,**不能全局调**:work_mem 是「每个排序/哈希节点」的上限,
// 不是每连接。生产 RDS 只有约 7GB 内存(shared_buffers 1.83GB 反推),全局设大值
// 遇上几十个并发连接同时排序会把内存吃穿。SET LOCAL 出了事务自动还原,
// Web API 的连接完全不受影响;且场景查询是串行的,同一时刻只有一条在跑,峰值可控。
//
// ⛔ 别再去加「信号码部分索引」——(content->>'code') 上的部分索引在测试机和生产
// 都实测过,生产上 −5% 落在噪声里(同配置两次测量本身差 6%),不值得引入
// 一个 Prisma 管不到的索引。详见 [[GAP_FLAGS_BY_PRIMARY]] 附近的耗时归因注释。
const [, rows] = await this.prisma.$transaction([
this.prisma.$executeRaw`SET LOCAL work_mem = '256MB'`,
this.prisma.$queryRaw<HitRow[]>(scenarioSql),
]);
const sqlMs = Date.now() - sqlStart; const sqlMs = Date.now() - sqlStart;
const postStart = Date.now(); const postStart = Date.now();
......
...@@ -62,7 +62,12 @@ function makeSqlCapturingPrisma(): { prisma: PrismaService; sql: () => string } ...@@ -62,7 +62,12 @@ function makeSqlCapturingPrisma(): { prisma: PrismaService; sql: () => string }
captured.push(parts.map((s, i) => s + (i < values.length ? flatten(values[i]) : '')).join('')); captured.push(parts.map((s, i) => s + (i < values.length ? flatten(values[i]) : '')).join(''));
return Promise.resolve([]); return Promise.resolve([]);
}; };
const prisma = { $queryRaw: queryRaw } as unknown as PrismaService; // 主查询走 `$transaction([SET LOCAL work_mem, $queryRaw(sql)])`(2026-08-29 起,
// 为抬高 work_mem;见 scenario 里那段实测注释)。桩要同时认这三个:
// $queryRaw 负责捕获 SQL,$executeRaw 吞掉 SET LOCAL,$transaction 把数组按序解析。
const noop = () => Promise.resolve(0);
const tx = (arr: unknown[]) => Promise.all(arr as Promise<unknown>[]);
const prisma = { $queryRaw: queryRaw, $executeRaw: noop, $transaction: tx } as unknown as PrismaService;
return { prisma, sql: () => captured.join('\n/* --- next query --- */\n') }; return { prisma, sql: () => captured.join('\n/* --- next query --- */\n') };
} }
......
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