MySQL查询Fetch耗时远高于Duration 不同参数下Fetch差异原因咨询
MySQL Fetch 耗时的核心计算逻辑
首先明确两个容易混淆的耗时指标的清晰边界:
- 观测到的Duration(服务器执行耗时),指的是客户端发起请求后,MySQL服务器完成查询解析、优化、数据检索、计算全流程,将整份结果集写入服务端侧的结果缓冲区(内存或磁盘临时表)所花费的时间,这个阶段不包含数据传输到客户端的开销。
- Fetch耗时,指的是服务端完成结果集准备后,逐块将结果从服务端缓冲区传输到客户端内存,直到客户端拉取完全量返回数据的总耗时,这个阶段的开销和服务端的查询执行过程完全解耦,不受之前的Duration长短直接影响。
也就是说,只要服务端能在5秒内把结果准备好,哪怕准备好的结果取出来往外传要花2分钟,测到的Duration也只会是5秒,和Fetch耗时完全独立。
不同查询参数导致Fetch耗时差异的核心原因与排查方向
既然两个参数返回的行数没有显著差异,那Fetch的差异一定不是行数本身导致的,要从影响“数据从服务端缓冲区到客户端的传输效率”的维度排查:
核心影响因素1:实际传输总数据量的差异
别只统计返回行数,要对比两个参数下返回结果的总字节数:
- 哪怕行数完全一样,如果两个条件匹配的行里,长文本、大二进制字段的占比、平均长度差很多,总传输字节数可能差1~2个数量级。比如第一个参数返回的1万行全是短数字、短字符串,总大小仅几MB;第二个参数返回的1万行里每行带几KB的长文本,总大小几百MB,传输耗时自然差几十倍。
- 排查方式:对返回的所有列做长度求和统计,直接对比两个参数下的总数据量,不要用行数近似代替数据量。
核心影响因素2:服务端结果缓冲区的存储介质差异
哪怕两个参数返回的总数据量差不多,也可能因为结果存储位置不同导致Fetch效率天差地别:
- 如果结果刚好被存在服务端的内存缓冲区里,取数时的读取速度是GB/s级别,可以快速喂给网络发送线程,Fetch自然快。
- 如果结果因为大小超过内存临时表的阈值,被刷到了磁盘临时表上,Fetch阶段每读一块数据都要触发磁盘IO,机械盘的随机读速度通常只有几十MB/s甚至更低,就算总数据量不大,也可能因为磁盘IO的瓶颈导致Fetch耗时飙升。
- 这种场景下,磁盘临时表的写入是在之前的Duration阶段完成的,测到的5秒Duration根本覆盖不到Fetch阶段读临时表的磁盘开销。
- 排查方式:在两个参数的查询执行前后,分别查看
SHOW SESSION STATUS LIKE 'Created_tmp_disk_tables'的计数变化,如果第二个参数执行后该值明显增长,就说明结果被写入了磁盘临时表。
核心影响因素3:网络传输的有效带宽利用率差异
同样的总字节数,网络传输的有效利用率不同,耗时也会差很多:
- MySQL是按行打包发送数据的,如果单行长度超过了TCP的MTU阈值,单一行就会被拆成多个TCP包传输,甚至触发包重传、窗口抖动,实际能打满的有效带宽可能只有峰值的10%~20%。
- 如果第一个参数返回的行都是短行,一次TCP包就能打包多行数据,带宽利用率能跑到80%以上;第二个参数返回的都是超长行,每一行就要拆好几个包,哪怕总数据量一样,传输耗时也会差好几倍。
- 排查方式:在Fetch阶段抓包看网卡的实际有效吞吐,如果吞吐远低于网卡的理论峰值,基本可以确定是拆包、重传、或者磁盘读瓶颈导致的发送速率上不去。
内容的提问来源于stack exchange,提问作者Ravinder Pulijala
相关产品推荐
相关产品推荐

