一、卡慢来源

1.分析:卡慢感受来源与原因

1.1 卡慢感受来源

  1. 用户使用前端页面卡慢
    • Web接口响应慢。
    • 前端渲染逻辑不合理(数据量大时阻塞)。
  2. 外部调用 ASM 服务卡慢
    • 对外 API 接口响应慢。

1.2 卡慢原因深度分析

(1)MongoDB 问题

  • 内存不足,缓存频繁淘汰,磁盘IO高
    • 大表(如 资产 表)未分片,导致分片间内存使用不均衡。
    • 索引过多且部分不合理,占用大量内存。
  • IO 压力大
    • 后台任务对 MongoDB 读写频率过高(需增加缓存机制)。
  • 锁竞争(SchemaLock)
    • 库表太多,导致 schemaLock 锁竞争严重,阻塞语句执行。
    • 解决措施:中台调整 WiredTiger Hash 桶;合并小表减少表数量。

(2)后端代码问题

  • 鉴权开销:每个接口都重复请求 auth 认证接口(~100ms)。
  • 级联树依赖:请求级联树接口(缓存过期后耗时 500-600ms)。
  • 接口功能臃肿
    • 单接口包含过多逻辑(如 on_list 查太多表)。
    • 资产组 接口被多处调用,需求不一但混用同一接口。
  • 业务逻辑复杂/不合理
    • 存在全表扫描统计(如资产清点)。
    • 循环查库代码。
    • 所属主机 逻辑冗余,几乎所有查询都带,导致业务复杂化。
  • 慢查询
    • 缺乏缓存(实时性不高的统计)。
    • 接口间依赖过重。

(3)前端代码问题

  • 前端逻辑不合理,导致后端返回快但前端渲染慢。

(4)特定环境问题

  • OpenAPI 频繁请求:请求过于频繁导致 CPU 飙升。
  • 解决措施:开分片;增加限流措施。

1.3 目标及判定标准

  1. 网上问题:实现 0 网上卡慢问题。
  2. 页面响应速度保5争3(不超5秒,最好3秒内)。
    • 判定标准:从第一个元素就绪到触发 load 事件的时间(或特定核心 DIV 可视)。
  3. 接口响应速度:简单接口 <500ms,复杂接口 <2s
    • 判定工具Server-Timing 响应头。
  4. 资源占用:降低 Mongo 读写 QPS 及内存占用。

2.优化思路总结

这是一个基于你提供的详细报告整理出的优化思路思维导图。

核心策略总结:

  1. 做减法:接口字段裁剪、移除冗余业务逻辑(如循环查库)。
  2. 做拆分:大接口拆解为多个独立小接口(资产列表)、同步转异步。
  3. 按需取:全量加载改为逐级/分页加载(资产树、OpenAPI)。
  4. 底座优化:Mongo分片、索引治理与锁冲突解决。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
flowchart TD
A[性能问题暴露]

A --> B[核心问题类型]
B --> B1[请求基础成本高]
B --> B2[全量数据返回]
B --> B3[接口职责过重]

B --> C[统一优化方法]
C --> C1[接口拆分]
C --> C2[按需加载]
C --> C3[分页 / 游标]
C --> C4[减少 Join]

C --> D[关键接口改造]
D --> D1[资产组树<br/>Tree 按需加载]
D --> D2[资产列表<br/>主列表瘦身]
D --> D3[获取资产组名<br/>全名可选 + 分页]
D --> D4[来源设备信息<br/>去循环 + 合并分页]

D --> E[最终效果]
E --> E1[接口耗时 200-300ms]
E --> E2[性能稳定性提升]
E --> E3[可扩展性增强]

二、MongoDB 索引治理

索引过多不仅会占用大量的内存(WiredTiger 缓存),还会严重拖慢写入性能(Insert/Update/Delete)。为了解决当前索引冗余、顺序混乱以及查询效率低下的问题,请遵循以下原则:

“先规范数据(去 $or),再复用索引(找前缀),最后才考虑新增。”

1. 索引设计核心原则 (ESR)

创建复合索引时,字段顺序必须按照 E -> S -> R 的优先级进行排列:

  1. **Equal (等值查询)**:最先放置。如 平台id所属主机 等。
  2. **Sort (排序字段)**:中间放置。如果有 sort 操作,字段必须紧跟在等值字段后。
  3. **Range (范围查询)**:最后放置。如 $gt$lt$in$ne 等。

2. 索引瘦身与复用原则

禁止盲目新增索引,优先考虑覆盖与去冗:

  • 利用最左前缀匹配:如果已有索引 {A: 1, B: 1, C: 1},则无需再为 {A: 1}{A: 1, B: 1} 单独建索引。宽索引可以兼容窄索引的查询。
  • 避免唯一标识冗余:如果 {平台id, 资产_id} 已经能唯一锁定记录,则禁止再为此组合增加 所属主机 等额外字段的复合索引。
  • 全局视角:新增索引前,必须检查现有索引列表。如果通过给现有索引末尾追加字段能解决问题,则不要新建。

3. 数据建模与状态规范(针对 OR 查询问题)

禁止在代码中使用复杂的 $or 来兼容多种“初始态/空态”:

  • 状态唯一性:一种业务含义只能对应一个值。禁止同时用 null0""None 表示同一种状态。
  • 必须设置默认值:所有新字段在设计时必须明确 Default Value
    • ❌ 错误写法:使用 {"$exists": false}$or 兼容各种空值(这会导致索引扫描效率极低)。
    • ✅ 正确写法:入库时字段必须存在。查询未处理状态统一使用 {"用户认领状态": 0}
  • 严禁逻辑发散:不要在代码里去猜数据可能有哪些空值(如 []{}),应在写入端保证数据的清洁和规范。

使用 Partial Index 避免无效数据

7. 性能对比:

4. 流程规范:先评审,后上线

  1. 索引评审制:涉及 MongoDB 索引的新增或修改,必须在文档中列出查询语句并说明理由。
  2. Explain 分析:开发者在申请索引前,需自行执行 .explain("executionStats")
    • 关注点:使用了索引时的状态为stage: 'IXSCAN'totalKeysExamined(扫描索引数)应尽可能接近 nReturned(返回结果数)。
  3. 定期清理:DBA 或 Owner 会定期统计索引使用率(db.collection.aggregate([ { $indexStats: {} } ])),对于 Ops(使用次数)为 0 的索引将直接下线。

三、web 接口治理

1. 背景与公共问题

主要性能瓶颈

  • Auth 认证:每次请求固定约 100ms
  • 资产组树缓存失效:单次请求 500–600ms
  • 历史接口问题
    • 大接口一次性返回全部数据
    • 多表 Join / 级联查询过多
    • 前端一次性渲染压力大

统一规范

  • 所有接口 必须携带 平台id
  • 禁止使用 "all" 作为默认查询条件(除非明确支持)

2. 资产组树接口重构(Tree 按需加载)

改造目标

  • 避免一次性加载全量资产组树
  • “全量返回” → “逐级加载”

核心改动

  • 接口语义升级为 Tree 模式
  • 页面行为调整为:
    • 首次只请求第一层
    • 点击展开节点时,再请求子节点

效果

项目 优化前 优化后
接口耗时 8–9s ~300ms
加载方式 全量 按需
用户体验 卡顿 秒开

3. 资产列表接口重构(拆接口)

改造目标

  • 列表接口只做“核心资产数据查询”
  • 所有关联信息拆成独立接口

核心策略

  • List 主接口
    • 只查 资产 表 + 必要配置表
    • 不再 Join 资产组 / 组织 / 平台 / 业务等信息
  • 关联信息
    • 按需调用
    • 前端或中台统一聚合

拆分出的独立能力

  • 资产组名称 / 全名
  • MAC 来源
  • 组织架构全名
  • 资产认领信息
  • 业务组名称
  • 平台名称

性能收益

环境 优化前 优化后
~6s ~300ms
157万条数据 ~600ms ~200ms

4. 性能收益

维度 旧接口 (已弃用) 新接口 (推荐/合一分支) 性能提升
资产列表 URL ?method=GET ?method=GET 23倍
资产组树 URL ?method=get ?method=get&_style=tree 20倍+
资产组 响应耗时 7.9s ~ 9s 300ms ~ 420ms 显著减少延迟
On_list 响应耗时 2.38s 103ms 亚秒级实时返回
核心逻辑耗时 - 100ms (其余为Auth认证) 逻辑大幅精简
性能基线要求 无明确标准 资产组 < 500ms / On_list < 200ms 建立硬性准入基线
  • 接口平均耗时下降 5~30 倍
  • 核心列表接口稳定在 200–300ms
  • 架构从「胖接口」演进为「能力解耦型接口」
  • 为后续缓存 / 并发 / 异步聚合提供空间