Mysql一条多表关联SQL把CPU打爆了,如何优化

文章讲述了在数据库CPU资源被大量消耗的情况下,通过分析异常SQL的执行计划,发现全表扫描和无谓词关联导致的性能问题。通过改写SQL,提前过滤无效数据,以及调整表的关联顺序(小表驱动大表),成功将执行时间从9秒降至0.00秒,显著提升了查询效率。

今天是清明假期的第三天,收到同事的求助,DB的CPU被打爆了!

查看监控,CPU已经被打爆100%

登录mysql,DB无锁阻塞,元凶是一个异常sql,存在39个并发执行。

SQL的明细如下:

select TEMPSALE.USER_ID_BUY,       TEMPSALE.ORDER_AMOUNT,       TEMPSALE.LAST_UPDATED_DATEfrom T_EAC_BU_SG_CO_INFO TSIrightjoin  (select TOI.SALE_PRO_ID,          TOI.USER_ID_BUY,          TOI.ORDER_AMOUNT,          TOI.LAST_UPDATED_DATE   from T_EAC_BU_ORDER_INFO TOI   left join T_EAC_BU_SALE_GOOD_INFO TEC on TOI.SALE_PRO_ID = TEC.SALE_PRO_ID) TEMPSALE on TSI.SALE_PRO_ID = TEMPSALE.SALE_PRO_IDwhere TSI.CAR_ORIGIN_CODE= '000000008671c3180186829f41ad336f' ;                                

首先看看该sql的执行计划吧

相关的表结构如下:

分析:

先来看看正常情况下这条sql单次执行耗时为多少

索引有缺失吗?

相关表的索引创建无大碍,关联字段与where谓词字段都已经创建好了索引。

针对这条异常sql,我们还有优化的空间吗??

解读执行计划,扫描 表别名为 TOI的表,全表扫描估算数据量为514049, Extra  列为 NULL 没有任何的谓词过滤无效数据。扫描后的结果与 表别名为 TEC 的表 Using index (SALE_PRO_ID字段)进行关联。再与 表别名为 TSI 的表 Using where 使用谓词过滤后关联。

执行耗时绝大部分耗时都在第一步与第二步,即 from T_EAC_BU_ORDER_INFO TOI   left join T_EAC_BU_SALE_GOOD_INFO TEC on TOI.SALE_PRO_ID = TEC.SALE_PRO_ID ,在不影响语义的前提下,

我们改写sql把能过滤大部分数据的谓词表提前作为驱动表执行,这样就能最大程度提高执行效率,缩短执行时间了。即,我们将原本 T_EAC_BU_ORDER_INFO 与 T_EAC_BU_SALE_GOOD_INFO 无谓词的关联,改写为 T_EAC_BU_ORDER_INFO 与 T_EAC_BU_SG_CO_INFO 带谓词关联。

改写后的sql

未优化前sql与优化后sql执行时间对比

未优化前sql与优化后sql执行计划对比

至此,原本执行耗时由9秒+的问题sql,优化至0.00秒,性能提升杠杠的。

总结:

1、多表关联,表谓词能提前的提前过滤无效数据

2、多表关联,小表驱动大表

3、大部分sql优化为最佳索引缺失,部分sql优化需要改写sql

优化包含千万条记录的 MySQL 数据库查询性能,需要从个维度进行调整和优化,包括索引设计、SQL 编写、结构优化、服务器配置等。以下是详细的优化策略: ### 优化索引设计 1. **选择高选择度的列创建索引** 在区分度高的列(如主键、唯一键)上创建索引,可以显著提升查询效率。对于低选择度的列(如性别、状态),创建索引的效果有限。 2. **避免在索引列上使用函数或达式** 在索引列上使用函数(如 `DATE(created_at)`)会导致索引失效,应尽量避免。 3. **关联时确保关联字段类型一致并建立索引** 连接时,关联字段类型必须一致,否则可能导致无法使用索引。 4. **使用组合索引代替个单列索引** 对于条件查询,组合索引比个单列索引更高效。例如:`WHERE a = ? ORDER BY b, c` 可以创建索引 `(a, b, c)`。 5. **避免全模糊匹配(如 `%value%`)导致的索引失效** 对于全模糊查询,可以考虑添加额外的高选择度字段作为过滤条件,或者使用全文索引[^1]。 ### 优化 SQL 查询语句 1. **减少不必要的数据扫描** 使用 `LIMIT` 限制返回行数,避免返回大量数据,尤其是在分页查询时。 2. **避免使用 `SELECT *`** 明确指定需要查询的字段,减少不必要的数据传输和内存消耗。 3. **使用 `EXPLAIN` 分析查询执行计划** 通过 `EXPLAIN` 查看查询是否使用了正确的索引,是否进行了全扫描。 4. **避免在 `ORDER BY` 和 `GROUP BY` 中频繁排序** 尽量在索引中包含排序字段,并确保排序顺序与索引顺序一致,以减少排序开销。 5. **合理使用分页查询** 对于大数据量的分页查询,使用 `LIMIT offset, size` 时,`offset` 过大会导致性能下降,可考虑使用基于游标的分页方式[^1]。 ### 使用性能分析工具 1. **使用 `SHOW PROFILES` 查看 SQL 耗时** 通过 `SHOW PROFILES` 查看每条 SQL 的执行时间,定位性能瓶颈。 2. **使用 `SHOW PROFILE` 查看 SQL 各阶段耗时** 通过 `SHOW PROFILE FOR QUERY query_id` 查看指定 SQL 的详细执行阶段耗时。 3. **使用 `SHOW PROFILE CPU` 查看 CPU 使用情况** 通过 `SHOW PROFILE CPU FOR QUERY query_id` 查看 SQL 执行时的 CPU 消耗情况[^2]。 ### 结构与存储优化 1. **合理设计结构** 避免冗余字段,合理使用范式与反范式设计,减少不必要的 JOIN 操作。 2. **使用合适的数据类型** 选择合适的数据类型可以减少存储空间和提高查询效率,例如使用 `TINYINT` 代替 `ENUM`,使用 `CHAR` 代替 `VARCHAR`(固定长度)。 3. **分区优化** 对于千万级数据,可以考虑使用分区,按时间或范围进行分区,减少单个分区的数据量,提高查询效率。 4. **定期优化** 使用 `OPTIMIZE TABLE` 定期整理碎片,提升 I/O 性能。 ### 服务器配置优化 1. **调整缓冲池大小** 增大 `innodb_buffer_pool_size` 可以显著提升 InnoDB 的查询性能,建议设置为物理内存的 50%-80%。 2. **调整连接数限制** 适当调整 `max_connections`,避免连接导致资源竞争。 3. **开启慢查询日志** 通过慢查询日志(`slow_query_log`)定位执行时间较长的 SQL,并进行针对性优化。 4. **使用连接池** 使用连接池(如 `c3p0`、`HikariCP`)减少连接创建和销毁的开销。 ### 示例:优化一个复杂查询 假设有一个 `mixpanel_data`,包含字段 `email`, `created_at`, `action`,需要查询特定时间段内的 `PauseAudio` 操作记录: ```sql SELECT * FROM `mixpanel_data` WHERE `email` != '' AND `created_at` BETWEEN '2019-03-24' AND '2020-03-24' AND `action` IN ('PauseAudio'); ``` 优化建议: - 在 `created_at` 和 `action` 上创建组合索引: ```sql CREATE INDEX idx_created_at_action ON mixpanel_data(created_at, action); ``` - 如果查询字段较,可以考虑使用覆盖索引,减少回查询。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值