docs(perf): 沉淀生产服务器高负载排查与MySQL任务排序优化实证报告
1. 记录Postgres JIT导致CPU飙升197%的排查与禁用优化; 2. 记录MySQL任务列表跨表排序引发Using filesort与Out of sort memory的根因诊断; 3. 记录新增account_assigned_to与复合索引落地、24万历史任务回填及线上实测87倍提速实证。
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# 生产服务器高负载排查与优化落地实证报告
|
||||
|
||||
- **记录时间**: 2026-10-03
|
||||
- **服务器环境**: 远程生产机 `47.114.114.225`(主机别名:`order+douyu_huya`),8 核 CPU / 16GB 内存
|
||||
- **涉及容器服务**:
|
||||
- `live-hub-py-douyu-login-1` / `live-hub-py-mysql-1` (当前 live-hub-py 服务)
|
||||
- `order_site-postgres-1` / `order_site-backend-1` (工单平台 Node.js / PG)
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心状态登记簿 (Status Registry)
|
||||
|
||||
| 类别 | 项目 | 现状描述 / 数据实证 | 确信度 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **已知 (Known)** | **Postgres 偶发 CPU 197%** | JIT 编译器在复杂聚合统计查询(`COUNT/FILTER`)时产生巨大的即时编译开销,单次耗时达 8.8s。执行 `ALTER SYSTEM SET jit = off;` 后,查询耗时直接降到 369ms(提速 24 倍)。 | 100% (已验证) |
|
||||
| **已知 (Known)** | **MySQL 任务全表 filesort 尖刺** | 客服列表轮询接口 `GET /api/douyu/tasks?handbook_scope=elite`,跨表 JOIN `accounts` 过滤后按 `id DESC LIMIT 100` 排序。导致 24 万条历史任务中符合条件的 1.1 万条全部写入临时表并产生 `Using filesort`,MySQL 周期性冲到 111.45% CPU,日志频繁报 `Out of sort memory`。 | 100% (已验证) |
|
||||
| **已知 (Known)** | **工单后台 Node.js 周期波峰** | `order_site-backend-1` 内有拼多多同步、打手抢单等后台调度(约 15~20s 周期),瞬时聚合打到 PG,形成系统的“忽高忽低”。 | 95% (日志证实) |
|
||||
| **目标 (Target)** | **彻底消除 MySQL 全表排序** | 将 `account_assigned_to` 冗余到 `douyu_tasks` 表,建立 `(account_assigned_to, handbook_scope, id)` 复合索引,消除 JOIN 与 filesort,降为单表倒序索引扫描。 | 100% (已达成) |
|
||||
| **目标 (Target)** | **零事故安全热更上线** | 完成数据库平滑迁移(24 万行数据回填与索引建立),发布 `live-hub-py` 容器,恢复业务调度与监控。 | 100% (已达成) |
|
||||
| **未知/待观察 (Unknown)** | **工单平台 Node/PG 定时聚合** | `order_site-postgres-1` 的业务慢查询(如未带索引的统计)仍存在周期性 CPU 波动(需工单系统代码级加索引优化,与 live-hub-py 相互隔离)。 | 90% |
|
||||
|
||||
---
|
||||
|
||||
## 2. 优化方案设计与实施
|
||||
|
||||
### 2.1 数据库结构升级与迁移
|
||||
|
||||
迁移文件:`web/backend/migrations/versions/20261003_0040_douyu_tasks_account_assigned_to.py`
|
||||
1. 为 `douyu_tasks` 添加 `account_assigned_to` 列(外键关联 `users.id`);
|
||||
2. 历史数据回填:
|
||||
```sql
|
||||
UPDATE douyu_tasks dt
|
||||
JOIN accounts a ON a.id = dt.account_id
|
||||
SET dt.account_assigned_to = a.assigned_to;
|
||||
```
|
||||
3. 建立覆盖排序的高性能复合索引:
|
||||
- `ix_douyu_tasks_assigned_scope_id (account_assigned_to, handbook_scope, id)`
|
||||
- `ix_douyu_tasks_account_assigned_to_id (account_assigned_to, id)`
|
||||
|
||||
### 2.2 业务查询与状态同步改造
|
||||
|
||||
1. **创建任务时自动注入**:
|
||||
在 `douyu_service.py` 的 `create_douyu_tasks()` 中,创建 `DouyuTask` 时自动带入当前账号的 `assigned_to`。
|
||||
2. **账号分配变更时级联更新**:
|
||||
在 `accounts.py` 的 `assign_account()` 中,当管理员将账号移交给新客服时,同步更新该账号下任务的 `account_assigned_to`。
|
||||
3. **彻底消除跨表排序**:
|
||||
在 `douyu.py` 的 `_visible_tasks_query()` 中,非管理员查询直接使用单表条件:
|
||||
```python
|
||||
# 优化前:需要 JOIN Account 表
|
||||
# query = query.join(Account).filter(Account.assigned_to == current.id)
|
||||
# 优化后:纯单表条件,走复合索引
|
||||
query = query.filter(DouyuTask.account_assigned_to == current.id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 部署上线与实测效果验证
|
||||
|
||||
### 3.1 迁移与部署过程
|
||||
- **镜像构建**: 多阶段构建完成,前端 dist 与后端代码更新打包。
|
||||
- **历史数据回填**: 生产环境共回填 239,118 条历史任务的所属客服数据。
|
||||
- **alembic_version**: 成功推进到 `20261003_0040`。
|
||||
|
||||
### 3.2 优化前后量化对比
|
||||
|
||||
| 指标维度 | 优化前状态 | 优化后状态 (生产实测) | 改善幅度 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **查询执行计划 (EXPLAIN)** | `Using temporary; Using filesort` | `Backward index scan` | **彻底消除 filesort** |
|
||||
| **索引命中** | 仅命中 `account_id`,跨表无法索引排序 | 命中 `ix_douyu_tasks_assigned_scope_id` (len 71) | **直接命中最优复合索引** |
|
||||
| **单次查询耗时** | 400 ~ 500 ms | **5.7 ms (0.0057s)** | **提速 ~87 倍** |
|
||||
| **MySQL 排序内存报错** | 频繁报 `Out of sort memory` | **0 次报错**,完全脱离 sort buffer | **彻底根治** |
|
||||
| **MySQL 容器瞬时 CPU** | 周期性拉升至 **111.45%** | 平稳维持在 **1.86% ~ 11%** | **降低 90%+** |
|
||||
| **系统平均负载 (load avg)** | 峰值 12.8 ~ 14.5 | 逐步平稳回落至 7.6 ~ 8.8 | **系统稳定性显著增强** |
|
||||
|
||||
### 3.3 生产线上业务运转状态
|
||||
- `/api/health` 检查:`{"status":"ok"}`
|
||||
- WebSocket 连接:客服工作台正常连接,斗鱼批量赠送精英令、改名、改密任务均在正常并发执行,状态流转顺畅。
|
||||
Reference in New Issue
Block a user