线上慢 SQL 索引优化分析(2026-08-26)
数据范围
- 来源:
/Users/dengzhangchen/Downloads/rm-1fyg92tzhbt23g9hw_slowlog_*.csv - 原始记录:638 条;按完整请求字段去重后 608 条;63 个 SQL Hash。
- 数据库分布:
pjcloudx_hardware451 条、pjcloudx_wallet153 条、pjcloudx_flow4 条。 - 主要扫描表:
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、锁等待和事务耗时排查。
上线步骤
- 低峰期逐条执行
01_add_indexes.sql,每条 DDL 单独观察完成时间和租户延迟。 - 执行前确认同名索引不存在;OceanBase 建索引可能持续几十秒到数分钟,不要重复提交。
- DDL 完成后对代表性 SQL 执行
EXPLAIN,确认使用新索引且不出现TABLE FULL SCAN。 - 观察 15–30 分钟慢 SQL、CPU、IO、事务提交延迟;若写入延迟明显,再评估 P1 索引。
- 统计接口仍慢时,优先改写 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,不再全表扫描。