55 lines
5.3 KiB
Markdown
55 lines
5.3 KiB
Markdown
# 线上慢 SQL 索引优化分析(2026-08-26)
|
||
|
||
## 数据范围
|
||
|
||
- 来源:`/Users/dengzhangchen/Downloads/rm-1fyg92tzhbt23g9hw_slowlog_*.csv`
|
||
- 原始记录:638 条;按完整请求字段去重后 608 条;63 个 SQL Hash。
|
||
- 数据库分布:`pjcloudx_hardware` 451 条、`pjcloudx_wallet` 153 条、`pjcloudx_flow` 4 条。
|
||
- 主要扫描表:`hard_order`、`hard_after_sale`、`wallet_transaction`、`act_hi_actinst`。
|
||
- 测试连接:OceanBase CE 4.3.5.5,MySQL 兼容模式;读取 `SHOW CREATE TABLE`、`SHOW INDEX`、`EXPLAIN`,并在测试库临时创建/回滚候选索引;未在生产库执行 DDL。
|
||
|
||
## 结论
|
||
|
||
### 强推荐,先上线
|
||
|
||
| 优先级 | 数据库/表 | 建议索引 | 主要覆盖日志 | 依据 |
|
||
|---|---|---|---|---|
|
||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, create_time, user_id)` | 订单列表、分页 COUNT,Hash `270048891%7C1391`、`528608244%7C616` 等 | 现有索引的第三列是 `merchant_id` 或 `user_id`,无法同时支撑租户+删除标记后的时间倒序;临时索引验证后由扫描+TOP-N 变为反向范围扫描。 |
|
||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, order_no, order_type, order_status)` | 订单号精确 COUNT,Hash `_1019382644%7C138` | 原执行计划只能在统计索引上按 `order_status` 扫描,再回表过滤订单号/类型;临时索引验证为三列等值范围扫描。 |
|
||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, android_order_code)` | 设备订单号查询,Hash `_986870313%7C628` | 原计划扫描约 98 万行后回表过滤 `android_order_code`;临时索引验证为等值范围扫描。 |
|
||
| P0 | `pjcloudx_hardware.hard_after_sale` | `(tenant_id, order_id, id)` | 订单列表中的“每单最新售后”子查询,覆盖 233 条相关记录 | 原表只有主键,`GROUP BY order_id + MAX(id)` 为全表扫描;临时索引验证后为有序范围扫描并使用 Merge Group By。 |
|
||
| P0 | `pjcloudx_wallet.wallet_transaction` | `(tenant_id, del_flag, create_time, user_id)` | 无业务类型条件的钱包流水首页,Hash `698590166%7C408` | 原计划全表扫描并排序;临时索引验证后为反向范围扫描,直接取前 10 行。 |
|
||
| P0 | `pjcloudx_wallet.wallet_transaction` | `(tenant_id, del_flag, biz_type(8), create_time, user_id)` | 钱包流水按业务类型筛选列表/COUNT,Hash `2036241023%7C466`、`255738862%7C620` 等 | `biz_type` 是 `TINYTEXT`,必须使用前缀索引;临时索引验证后可按租户、删除标记、业务类型及时间范围扫描。 |
|
||
| P0 | `pjcloudx_flow.act_hi_actinst` | `(ASSIGNEE_, END_TIME_)` | 流程历史待办查询,Hash `_2096433074%7C126` | 原计划全表扫描并排序;临时索引验证后为反向范围扫描,直接满足 `ORDER BY END_TIME_ DESC LIMIT 10`。 |
|
||
|
||
### 条件推荐,先观察资源与收益
|
||
|
||
| 优先级 | 建议索引 | 适用场景 | 说明 |
|
||
|---|---|---|---|
|
||
| P1 | `pjcloudx_hardware.hard_order (tenant_id, del_flag, order_status, pay_time, merchant_id)` | 按支付状态+支付时间汇总,Hash `_1655261703%7C470` 等 | 临时索引可将范围缩小到状态/时间,但仍需要回表和聚合;建议 P0 上线后按 `EXPLAIN` 和实际耗时决定。 |
|
||
| P1 | `pjcloudx_hardware.hard_order (tenant_id, del_flag, merchant_dept_id, order_status, merchant_type, pay_time)` | 指定商户部门的支付统计,Hash `1634334540%7C358` 等 | 对指定部门过滤收益明显;宽索引写放大较大,先对高频租户/接口评估。 |
|
||
| P1 | `pjcloudx_wallet.wallet_manage (tenant_id, dept_id, id)` | 钱包按部门聚合 | 仅用于小维表定位钱包 ID;表规模较小时收益有限,适合与钱包统计接口一起验证。 |
|
||
|
||
## 暂不建议只靠加索引解决
|
||
|
||
- 统计类 SQL 使用派生表、外层权限过滤和 `ORDER BY CASE`,索引只能减少底层扫描,不能消除派生表物化/聚合成本;建议后续改为先过滤再聚合,并移除 COUNT 查询中的无意义排序。
|
||
- `hard_order` 订单详情列表反复 `LEFT JOIN` 最新售后子查询;P0 索引后仍建议将“最新售后 ID”改为窗口/预聚合或单独维护字段。
|
||
- `UPDATE hard_door_cabinet ... WHERE id = ? AND del_flag = ? AND tenant_id = ?` 已命中主键 `id`,慢点更可能是锁等待、事务或网络;加索引不能解决,需结合 `SHOW PROCESSLIST`、锁等待和事务耗时排查。
|
||
|
||
## 上线步骤
|
||
|
||
1. 低峰期逐条执行 `01_add_indexes.sql`,每条 DDL 单独观察完成时间和租户延迟。
|
||
2. 执行前确认同名索引不存在;OceanBase 建索引可能持续几十秒到数分钟,不要重复提交。
|
||
3. DDL 完成后对代表性 SQL 执行 `EXPLAIN`,确认使用新索引且不出现 `TABLE FULL SCAN`。
|
||
4. 观察 15–30 分钟慢 SQL、CPU、IO、事务提交延迟;若写入延迟明显,再评估 P1 索引。
|
||
5. 统计接口仍慢时,优先改写 SQL,再考虑继续增加宽复合索引。
|
||
|
||
## 验证记录
|
||
|
||
临时索引均已在测试库创建并回滚,最终检查无 `tmp_%` 索引残留。验证结果:
|
||
|
||
- 订单时间分页:`TABLE RANGE SCAN ... Reverse`,不再全表扫描/大范围 TOP-N。
|
||
- 钱包业务流水:`TABLE RANGE SCAN ... Reverse`,`biz_type(8)` 前缀索引可用。
|
||
- 最新售后:`MERGE GROUP BY` + `TABLE RANGE SCAN`,不再全表扫描。
|
||
- 流程历史:`TABLE RANGE SCAN ... Reverse`,不再全表扫描。
|