MySQL Sending data导致查询很慢的问题详细分析

本文通过explain、showprocesslist及showprofile等工具定位了一个MySQL查询缓慢的问题,最终发现是由于大字段varchar导致的性能瓶颈,并给出了优化建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天帮忙定位一个MySQL查询很慢的问题,定位过程综合各种方法、理论、工具,很有代表性,分享给大家作为新年礼物:)

 

【问题现象】

使用sphinx支持倒排索引,但sphinx从mysql查询源数据的时候,查询的记录数才几万条,但查询的速度非常慢,大概要4~5分钟左右

 

【处理过程】

1)explain

首先怀疑索引没有建好,于是使用explain查看查询计划,结果如下:

从explain的结果来看,整个语句的索引设计是没有问题的,除了第一个表因为业务需要进行整表扫描外,其它的表都是通过索引访问

 

2)show processlist;

explain看不出问题,那到底慢在哪里呢?

于是想到了使用 show processlist查看sql语句执行状态,查询结果如下:

发现很长一段时间,查询都处在 “Sending data”状态

查询一下“Sending data”状态的含义,原来这个状态的名称很具有误导性,所谓的“Sending data”并不是单纯的发送数据,而是包括“收集 + 发送 数据”。

这里的关键是为什么要收集数据,原因在于:mysql使用“索引”完成查询结束后,mysql得到了一堆的行id,如果有的列并不在索引中,mysql需要重新到“数据行”上将需要返回的数据读取出来返回个客户端。

 

3)show profile

为了进一步验证查询的时间分布,于是使用了show profile命令来查看详细的时间分布

首先打开配置:set profiling=on;
执行完查询后,使用show profiles查看query id;
使用show profile for query query_id查看详细信息;

结果如下:

从结果可以看出,Sending data的状态执行了216s

 

4)排查对比

经过以上步骤,已经确定查询慢是因为大量的时间耗费在了Sending data状态上,结合Sending data的定义,将目标聚焦在查询语句的返回列上面

经过一 一排查,最后定为到一个description的列上,这个列的设计为:`description`varchar(8000) DEFAULT NULL COMMENT '游戏描述',

于是采取了对比的方法,看看“不返回description的结果”如何。show profile的结果如下:

可以看出,不返回description的时候,查询时间只需要15s,返回的时候,需要216s,两者相差15倍

 

【原理研究】

至此问题已经明确,但原理上我们还需要继续探究。

这篇淘宝的文章很好的解释了相关原理:innodb使用大字段text,blob的一些优化建议

这里的关键信息是:当Innodb的存储格式是 ROW_FORMAT=COMPACT (or ROW_FORMAT=REDUNDANT)的时候,Innodb只会存储前768字节的长度,剩余的数据存放到“溢出页”中。

我们使用show table status来查看表的相关信息:

可以看到,平均一行大约1.5K,也就说大约1/10行会使用“溢出存储”,一旦采用了这种方式存储,返回数据的时候本来是顺序读取的数据,就变成了随机读取了,所以导致性能急剧下降。

 

另外,在测试过程中还发现,无论这条语句执行多少次,甚至将整个表select *几次,语句的执行速度都没有明显变化。这个表的数据和索引加起来才150M左右,而整个Innodb buffer pool有5G,缓存整张表绰绰有余,如果缓存了溢出页,性能应该大幅提高才对。

但实测结果却并没有提高,因此从这个测试可以推论Innodb并没有将溢出页(overflow page)缓存到内存里面。

这样的设计也是符合逻辑的,因为overflow page本来就是存放大数据的,如果也放在缓存里面,就会出现一次大数据列(blob、text、varchar)查询,可能就将所有的缓存都更新了,这样会导致其它普通的查询性能急剧下降。

 

【解决方法】

找到了问题的根本原因,解决方法也就不难了。有几种方法:

1)查询时去掉description的查询,但这受限于业务的实现,可能需要业务做较大调整

2)表结构优化,将descripion拆分到另外的表,这个改动较大,需要已有业务配合修改,且如果业务还是要继续查询这个description的信息,则优化后的性能也不会有很大提升。

转自:http://blog.youkuaiyun.com/yunhua_lee/article/details/8573621

<think>嗯,用户遇到了MySQL的'sending data'状态耗时过长的问题,需要解决。首先,我需要回忆一下'sending data'状态具体指的是什么。根据之前的知识,这个状态通常出现在查询执行期间,涉及到从储引擎读取数据并通过网络发送给客户端的过程。可能的原因有很多,比如查询效率低、索引缺失、数据量过大、网络问题或者服务器配置不当。 接下来,我应该按照系统级指令的要求,结构清晰、分步骤解答。需要引用提供的参考内容,比如引用[3]提到的使用LOAD DATA INFILE而不是INSERT来加快数据加载,这可能和优化数据导入有关,但用户的问题是关于查询阶段的,所以可能需要其他优化手段。 首先,分析问题可能的原因。用户提到'sending data'耗时过长,这通常意味着查询需要处理大量数据,或者在全表扫描。这时候,检查查询的执行计划是关键,查看是否有合适的索引被使用。比如,如果WHERE子句中的列没有索引,可能会导致全表扫描,增加数据读取时间,从而延长'sending data'状态。 然后,考虑索引优化。参考引用[3]中的优化建议,虽然没有直接提到索引,但合理的索引可以显著减少数据扫描量。需要建议用户添加必要的索引,尤其是复合索引,以覆盖查询所需的列。 接下来,优化查询语句。比如避免使用SELECT *,只选择需要的列,减少传输的数据量。同时,检查是否有不必要的JOIN或者子查询,这些都可能增加处理时间。此外,分页查询时使用LIMIT和OFFSET可能导致性能问题,可以改用基于游标的分页方法。 服务器和网络配置也是一个方面。比如调整max_allowed_packet参数,确保网络带宽足够,减少延迟。硬件方面,如果磁盘I/O成为瓶颈,升级SSD可能会有帮助。 另外,参考引用[1]中的show profiles,建议用户使用性能分析工具,如EXPLAIN或SHOW PROFILE,来定位具体耗时环节。比如,执行EXPLAIN分析查询计划,查看是否有全表扫描或临时表的使用。SHOW PROFILE可以显示各个阶段的耗时,确认'sending data'阶段的具体时间占比。 同时,需要考虑数据量的问题。如果单次查询返回大量数据,是否可以考虑分页或者分批处理。引用[3]提到的LOAD DATA INFILE虽然主要针对数据导入,但也提示了批量处理的重要性,可能间接影响查询效率。 最后,系统参数调优,比如调整innodb_buffer_pool_size,确保足够的内来缓数据和索引,减少磁盘I/O。连接线程配置如thread_cache_size也可能影响性能,但需要根据具体情况调整。 总结下来,解决方案应该包括检查执行计划、优化索引、精简查询、调整服务器配置、分析性能和使用工具定位问题。可能需要结合多个方面来综合解决'sending data'耗时过长的问题。同时,生成的相关问题要围绕这些方面,比如如何分析执行计划、优化索引的方法、服务器参数调整等。</think>### MySQL 'sending data' 状态耗时过长解决方案 `sending data` 是MySQL查询执行过程中的一个状态,表示服务器正在从储引擎读取数据并通过网络发送给客户端[^2]。若此阶段耗时过长,可通过以下步骤排查和优化: --- #### 一、问题定位 1. **分析执行计划** 使用 `EXPLAIN` 查看SQL的执行计划,重点关注以下字段: - `type`:若为 `ALL` 表示全表扫描,需优化索引 - `rows`:扫描的行数,数值过大需优化查询条件 - `Extra`:若出现 `Using temporary` 或 `Using filesort`,需调整查询或索引 示例: ```sql EXPLAIN SELECT * FROM payment WHERE customer_id = 100; ``` 2. **性能分析工具** 使用 `SHOW PROFILE` 或 `SHOW PROFILES` 定位耗时阶段(需先启用性能分析): ```sql SET profiling = 1; SELECT * FROM payment WHERE amount > 10; SHOW PROFILES; -- 查看Query_ID SHOW PROFILE FOR QUERY 1; -- 详细阶段耗时[^1] ``` --- #### 二、优化方法 1. **索引优化** - **添加缺失索引**:为 `WHERE`、`JOIN`、`ORDER BY` 涉及的列创建索引 ```sql ALTER TABLE payment ADD INDEX idx_customer_amount (customer_id, amount); ``` - **避免冗余索引**:定期使用 `SHOW INDEX FROM table` 分析索引使用率 2. **查询语句优化** - **减少返回数据量**:避免 `SELECT *`,仅选择必要字段 - **分页优化**:使用 `WHERE id > {last_id}` 替代 `LIMIT offset, size` - **避免复杂子查询**:改用 `JOIN` 或临时表 3. **服务器与网络优化** - **调整 `max_allowed_packet`**:增大网络传输包大小限制 - **检查网络延迟**:通过 `ping` 或 `traceroute` 分析客户端与服务器的网络质量 - **升级硬件**:使用SSD替代HDD,提高I/O性能 --- #### 三、系统参数调优 1. **InnoDB缓冲池** 调整 `innodb_buffer_pool_size`(通常设为物理内的70%~80%): ```ini [mysqld] innodb_buffer_pool_size = 4G ``` 2. **线程与连接配置** ```ini thread_cache_size = 16 -- 减少线程创建开销 max_connections = 200 -- 根据实际负载调整 ``` --- #### 四、其他建议 - **批量操作优化**:对大量数据写入使用 `LOAD DATA INFILE`(比 `INSERT` 快20倍)[^3] - **定期维护表**:执行 `OPTIMIZE TABLE payment` 整理碎片 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值