修性能回归:把「已并入 / 子段」判据做成派生列(visits.is_merged)
昨天给「已并入 / 子段」做的收口写成每行去 entity_raw 判 `raw->>'ZMERGEDTO' IS NOT NULL OR raw->>'ZPARENT' IS NOT NULL`,而 entity_raw.raw 是 **TOAST 过的整行 Core Data JSON**:一次探针要取回并解析整行。地点库「每个地点 × 5 个子查询」 的量级下 —— 实测 /places 70.1 秒、首页 43.6 秒才出页面(而库里没有任何一条 SQL 超过 1 秒: 时间全花在成千上万次「取整行再解析」上,进程 CPU 只用了 3s/135s)。 这批「渲染若干页面」的布局用例因此从 20 秒涨到 226~309 秒,整套验收从 6 分钟变 20+ 分钟。 - schema.sql:加派生列 visits.is_merged + 回填 + 给回填用的部分索引 (只索引确实带合并标记的那几十行,不用取回 raw)。 - stats.SoloVisitSQL 改成读这一列(判据仍然只此一份,读侧全部经由它)。 - 写入侧跟着刷:ingest 在 insertEntityRaw 之后、backup_merge 的 recountPlaces 里, 共用 stats.RefreshMergedFlagsSQL。 - 直接改留档里的合并标记(工具脚本 / 用例)之后也要刷一次,schema.sql 与函数注释都写明了。 实测:/places 70084ms → 206ms,/ 43633ms → 698ms(比「关掉判据」的对照还快)。 新增 page_render_budget_test.go:给 7 个关键页卡 10 秒上限的灾难探测器 (功能全对、只是慢的那种退化,之前没有任何用例会红)。
This commit is contained in:
+15
-7
@@ -112,18 +112,26 @@ func (w *where) andSQL() string {
|
||||
//
|
||||
// 真机把它**并入了别的到访**(ZVISIT.ZMERGEDTO),或它**只是某个父段的一部分**(ZVISIT.ZPARENT)时,
|
||||
// app 既不单独显示它、也不把它算进次数与停留——本站以前把这两种行当成普通到访,
|
||||
// 于是时间线上出现「同一时刻同一地点两条」(实测数据集 9 有 54 + 10 条,26 条的到达时间与原件完全相同),
|
||||
// 于是时间线上出现「同一时刻同一地点两条」(实测数据集 9 有 64 条,26 条的到达时间与原件完全相同),
|
||||
// 日停留也跟着重复计入。
|
||||
//
|
||||
// 判据取派生列 `visits.is_merged`(导入 / 合并时从 entity_raw 算好,见 RefreshMergedFlagsSQL):
|
||||
// 早先是每行去 entity_raw 取回**整行 Core Data JSON** 再解析 raw->>'ZMERGEDTO',
|
||||
// 而 raw 是 TOAST 过的整行——地点库「每个地点 × 5 个子查询」的量级下,一个页面实测要 70 秒。
|
||||
//
|
||||
// ⚠️ 只在**读**的一侧收口:导出包必须原样带着这些行(rond 靠它们认这次合并发生过),
|
||||
// 所以 internal/rond 与 backup_diff / backup_merge 一律不加这个条件。
|
||||
// 所以 internal/rond 与 backup_diff / backup_merge 的实体对齐一律不加这个条件。
|
||||
//
|
||||
// 判据只此一份,别在别处手抄——抄一份迟早跟这里漂移(区域限制那套就犯过一次)。
|
||||
func SoloVisitSQL(alias string) string {
|
||||
return `NOT EXISTS (SELECT 1 FROM entity_raw er WHERE er.dataset_id=` + alias + `.dataset_id` +
|
||||
` AND er.entity='ZVISIT' AND er.src_pk=` + alias + `.src_pk` +
|
||||
` AND (er.raw->>'ZMERGEDTO' IS NOT NULL OR er.raw->>'ZPARENT' IS NOT NULL))`
|
||||
}
|
||||
func SoloVisitSQL(alias string) string { return "NOT " + alias + ".is_merged" }
|
||||
|
||||
// RefreshMergedFlagsSQL 从 entity_raw 重算 visits.is_merged。
|
||||
// 导入、合并(以及任何直接改过留档里 ZMERGEDTO / ZPARENT 的操作)之后都要跑一次。
|
||||
const RefreshMergedFlagsSQL = `UPDATE visits v SET is_merged = TRUE
|
||||
FROM entity_raw er
|
||||
WHERE er.dataset_id = v.dataset_id AND er.entity = 'ZVISIT' AND er.src_pk = v.src_pk
|
||||
AND (er.raw->>'ZMERGEDTO' IS NOT NULL OR er.raw->>'ZPARENT' IS NOT NULL)
|
||||
AND v.dataset_id = $1 AND NOT v.is_merged`
|
||||
|
||||
// visitsWhere 构造作用于 visits 表别名 v 的过滤条件(**访客口径**:已并入 / 子段的到访不算数)。
|
||||
// off 用于嵌套:子查询里的占位符从 off+1 开始编号,避免与外层冲突。
|
||||
|
||||
Reference in New Issue
Block a user