一、SQL 执行计划分析
以下是当前 SQL 的执行计划分析:
plaintext
+----+-------------+-------------------------------+------------+------+-------------------------------------------------------------+-------------------------------+---------+-------------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------------------+------------+------+-------------------------------------------------------------+-------------------------------+---------+-------------+---------+----------+-------------+
| 1 | SIMPLE | ecif_point_subjects_info_2023 | p202312 | ref | ecif_point_subjects_info_idx2,ecif_point_subjects_info_idx3 | ecif_point_subjects_info_idx2 | 212 | const,const | 1378916 | 100.00 | Using index |
+----+-------------+-------------------------------+------------+------+-------------------------------------------------------------+-------------------------------+---------+-------------+---------+----------+-------------+
plaintext
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Limit/Offset: 5/300 row(s) (cost=342931.18 rows=5)
-> Group aggregate: count(1), sum(ecif_point_subjects_info_2023.debt_total_amt), sum(ecif_point_subjects_info_2023.crdt_total_amt) (cost=342931.18 rows=1378916)
-> Covering index lookup on ecif_point_subjects_info_2023 using ecif_point_subjects_info_idx2 (batch_date='2023-12-30', batch_id='20231230803') (cost=205039.58 rows=1378916)
|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
二、执行计划字段详解
1. 基础执行计划字段
id
:查询的唯一标识符。1
表示这是第一个查询(没有子查询或联合查询)。select_type
:查询类型。SIMPLE
表示这是一个简单的查询,不包含子查询或联合查询。table
:查询涉及的表名。ecif_point_subjects_info_2023
。partitions
:查询涉及的分区。p202312
表示查询只涉及2023-12
的数据分区。type
:连接类型。ref
表示 MySQL 使用了非唯一索引进行查找。possible_keys
:MySQL 可能使用的索引。这里有ecif_point_subjects_info_idx2
和ecif_point_subjects_info_idx3
。key
:MySQL 实际使用的索引。ecif_point_subjects_info_idx2
是实际使用的索引。key_len
:使用的索引长度。212
字节,这表明索引包含了多个字段(batch_date
,batch_id
,br_no
,tx_br_no
,debt_total_amt
,crdt_total_amt
)。ref
:显示索引的哪一列被使用。const,const
表示batch_date
和batch_id
被用作常量值进行查询。rows
:MySQL 估计需要扫描的行数。1378916
表示 MySQL 估计需要扫描大约 138 万行数据。filtered
:表示查询条件过滤后的行数比例。100.00
表示没有进一步的过滤。Extra
:额外的信息。Using index
表示 MySQL 使用了覆盖索引,不需要回表查询。
2. 树状执行计划分析
Limit/Offset: 5/300
:表示查询结果将被限制为从第 300 行开始,返回 5 行数据。Group aggregate
:表示 MySQL 正在执行GROUP BY
聚合操作,计算COUNT(1)
、SUM(debt_total_amt)
和SUM(crdt_total_amt)
。Covering index lookup
:表示 MySQL 使用了覆盖索引ecif_point_subjects_info_idx2
,直接从索引中获取数据,不需要回表查询。
三、性能分析
1. 索引使用分析
-
当前查询使用了索引
ecif_point_subjects_info_idx2
,该索引包含字段:sql(batch_date, batch_id, br_no, tx_br_no, debt_total_amt, crdt_total_amt)
-
查询条件
batch_date = '2023-12-30' AND batch_id = '20231230803'
完全匹配索引前缀,因此 MySQL 能够高效地定位数据。 -
GROUP BY br_no, tx_br_no
也匹配索引的前缀字段,因此可以利用索引进行排序,避免额外的排序操作。
2. 扫描行数分析
- 预计需要扫描 138 万行数据,这主要是因为:
GROUP BY
操作需要遍历所有符合条件的数据,进行聚合计算。LIMIT 300, 5
导致 MySQL 需要扫描前 300 行数据,然后丢弃这些数据,只返回最后 5 行。
3. 性能瓶颈
- 扫描行数大:虽然使用了覆盖索引,但扫描 138 万行数据仍然会带来较大的 I/O 和 CPU 开销。
- 深分页问题 :
LIMIT 300, 5
是典型的深分页场景,MySQL 需要扫描前 300 行数据并丢弃,效率较低。 - 聚合操作开销 :
GROUP BY
操作需要对大量数据进行分组和统计,增加了 CPU 消耗。
四、优化建议
1. 优化索引
- 确保索引字段匹配查询条件和排序字段 :
- 当前索引
ecif_point_subjects_info_idx2
已经覆盖了batch_date
,batch_id
,br_no
,tx_br_no
,debt_total_amt
,crdt_total_amt
,能够支持当前查询。 - 如果查询中还涉及其他字段(如
dept_no
, field2,bal_ind
),可以考虑扩展索引字段。
- 当前索引
2. 减少扫描行数
- 缩小查询范围 :如果业务允许,可以进一步缩小查询范围,例如增加
br_no
或tx_br_no
的过滤条件。 - 使用更细粒度的分区 :如果数据按
br_no
或tx_br_no
分布不均,可以考虑按这些字段进行子分区,以减少扫描的数据量。
3. 优化分页
- 避免深分页 :
-
使用游标分页(Cursor-based Pagination)代替
LIMIT offset, size
,例如:sqlSELECT batch_id, COUNT(1) AS countAll, ... FROM ecif_point_subjects_info_2023 WHERE batch_date = '2023-12-30' AND batch_id = '20231230803' AND (br_no, tx_br_no) > ('last_br_no', 'last_tx_br_no') GROUP BY br_no, tx_br_no ORDER BY br_no, tx_br_no LIMIT 5;
-
每次查询使用上一次查询的最后一条记录的
br_no
和tx_br_no
作为游标。
-
4. 缓存结果
- 对于频繁查询的聚合结果,可以使用缓存(如 Redis)来减少数据库的负载。
5. 批量计算
- 如果聚合结果用于报表展示,可以考虑在后台异步计算并存储结果,减少实时查询的压力。
五、总结
-
当前查询性能瓶颈:
- 扫描行数大(约 138 万行)。
- 深分页导致不必要的数据扫描。
- 聚合操作增加 CPU 开销。
-
优化方向:
- 优化索引,确保字段匹配。
- 使用游标分页减少深分页带来的性能问题。
- 缩小查询范围,减少扫描行数。
- 使用缓存或异步计算减少实时查询压力。
通过以上优化措施,可以显著提升查询性能,尤其是在大数据量和高并发场景下。