From d1694bf2454bb4ecc380773710a00791e478400e Mon Sep 17 00:00:00 2001 From: yueli Date: Fri, 11 Sep 2026 14:38:40 +0800 Subject: [PATCH] =?UTF-8?q?perf(stocktake):=20=E5=8E=BB=E6=8E=89=E5=BA=93?= =?UTF-8?q?=E4=BD=8D=E6=A0=91=E7=9A=84=E5=85=A8=E5=B1=95=E5=BC=80=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=EF=BC=8C=E5=B9=B6=E4=B8=BA=E6=8E=A8=E8=8D=90=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=A1=A5=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务反馈切到抽盘面板与点【获取推荐】时卡顿。实测定位: 【真正的原因在前端】先跑 EXPLAIN ANALYZE 排除了后端 —— 推荐查询在当前数据量下仅耗时 1.3ms,全走 Seq Scan + Hash Join, Buffers shared hit=161 全部命中缓存。所以卡顿来自 DOM: el-tree 原先带 default-expand-all,会把全库 3371 个库位节点一次性铺进 DOM。 去掉 default-expand-all 后初始只渲染根节点。勾选状态与 getCheckedNodes 不受影响 —— el-tree 的 Node store 会预先构建全树,展开与否只影响 DOM。 【索引仍补上,但说明白】实测 pg_indexes:stock_* 三表的 warehouse_location 已有索引,但 trans_outbound / trans_borrow 只有主键,时间字段与 (source_table, stock_id) JOIN 键都缺;三张库存表的时间字段也缺。 新增迁移 add_active_location_indexes.sql 补 7 个索引。 诚实说明写在迁移头部:当前数据量下加索引后本查询**仍会走 Seq Scan**, 这是小表下的正确计划,索引是为 90 天/半年后的数据增长做前瞻,不是修当前的慢。 【loading 状态】treeLoading / recLoading 已在既有实现中就位并闭环在 try/finally 中,分别绑在 el-tree 的 v-loading 与【获取推荐】按钮的 :loading 上, 本轮复核确认,未做无谓改名。 实测: 索引后执行计划仍为 Seq Scan,Execution Time 0.769ms(预期内) --- db_migrations/add_active_location_indexes.sql | 53 +++++++++++++++++++ .../src/views/stock/stocktake/index.vue | 5 +- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 db_migrations/add_active_location_indexes.sql diff --git a/db_migrations/add_active_location_indexes.sql b/db_migrations/add_active_location_indexes.sql new file mode 100644 index 0000000..b0a2840 --- /dev/null +++ b/db_migrations/add_active_location_indexes.sql @@ -0,0 +1,53 @@ +-- ============================================================================= +-- 一次性迁移:为「活跃库位推荐」查询补索引 +-- +-- 背景 +-- get_active_locations(company_name, days, top_n) 要扫三张流水/库存表的 +-- 时间字段,并逐一 JOIN 回库存表取库位: +-- trans_outbound.outbound_time → JOIN stock_* ON (source_table, stock_id) +-- trans_borrow.borrow_time → JOIN stock_buy ON (source_table, stock_id) +-- stock_buy.in_date / stock_semi.production_date / stock_product.production_date +-- +-- 现状(实测 pg_indexes): +-- stock_buy / stock_semi / stock_product 的 warehouse_location 已建索引 +-- trans_outbound、trans_borrow 只有主键索引,时间字段和 (source_table, +-- stock_id) JOIN 键都没有索引;三张库存表的时间字段也没有索引。 +-- +-- 为什么现在加(诚实说明) +-- 实测 EXPLAIN ANALYZE:当前数据量下(trans_outbound 900 行、stock_buy 2018 行) +-- 该查询耗时仅 1.3ms,且全走 Seq Scan + Hash Join —— 这是小表下的**正确** +-- 计划,索引也不会被选中。所以本次加索引不是为了修当前的慢,而是为 +-- 90 天/半年后的数据增长做前瞻,避免那时才临时补。 +-- 加完索引后本查询在当前数据量下仍会走 Seq Scan,属预期行为。 +-- +-- 执行: docker exec -i inventory_db psql -U test -d inventory_system < 本文件 +-- ============================================================================= +BEGIN; + +-- 出库:按时间窗口过滤 +CREATE INDEX IF NOT EXISTS ix_trans_outbound_time + ON trans_outbound(outbound_time); + +-- 出库:回查库存表取库位的 JOIN 键 +CREATE INDEX IF NOT EXISTS ix_trans_outbound_source_stock + ON trans_outbound(source_table, stock_id); + +-- 借用:按时间窗口过滤 +CREATE INDEX IF NOT EXISTS ix_trans_borrow_time + ON trans_borrow(borrow_time); + +-- 借用:回查库存表取库位的 JOIN 键 +CREATE INDEX IF NOT EXISTS ix_trans_borrow_source_stock + ON trans_borrow(source_table, stock_id); + +-- 入库没有独立流水表,直接以库存表自身的入库/生产时间计时 +CREATE INDEX IF NOT EXISTS ix_stock_buy_in_date + ON stock_buy(in_date); + +CREATE INDEX IF NOT EXISTS ix_stock_semi_production_date + ON stock_semi(production_date); + +CREATE INDEX IF NOT EXISTS ix_stock_product_production_date + ON stock_product(production_date); + +COMMIT; diff --git a/inventory-web/src/views/stock/stocktake/index.vue b/inventory-web/src/views/stock/stocktake/index.vue index 7c5f12d..809c20a 100644 --- a/inventory-web/src/views/stock/stocktake/index.vue +++ b/inventory-web/src/views/stock/stocktake/index.vue @@ -134,6 +134,10 @@
💡 提示:勾选父级货架,将自动涵盖其下属的所有末级库位及库存。
+