慢查询瓶颈在哪里?香港服务器MySQL的InnoDB参数与存储I/O如何排查
CPU不高,查询却越来越慢;磁盘利用率接近100%,换成更快的存储后改善仍不明显。这两种现象都说明:单个指标只能描述一个侧面,不能直接定位慢查询。香港服务器上的MySQL性能排查,应把应用响应时间、SQL耗时、CPU、内存、磁盘队列、事务等待和网络往返放在同一个时间窗口里,寻找“哪个指标先变化、哪些指标随后联动”。
真正要回答的问题不是“哪个InnoDB参数应该调大”,而是请求时间花在哪里:SQL执行消耗了CPU,缓存未命中触发了物理读,提交事务等待持久化,锁竞争让请求排队,还是应用与数据库之间的通信拖慢了整体响应。先确定瓶颈路径,再修改SQL、参数或存储配置,才能避免把局部优化变成新的资源压力。
一、建立观察窗口,让所有指标描述同一段时间
对齐时间、负载和请求类型
建议选取一次明确的变慢过程,覆盖正常阶段、延迟上升阶段和恢复阶段。例如连续观察15分钟,以10秒或30秒为一个聚合窗口,同时保留异常时段的秒级系统采样。应用、数据库和监控系统应校准时钟,并使用一致的时区展示。
平均值容易隐藏短时排队,因此至少同时记录请求量、P95响应时间、错误率和正在处理的请求数。数据库侧则应区分查询次数、事务提交次数、活跃执行线程和连接总数。
| 层次 | 同时采集的指标 | 主要回答的问题 |
|---|---|---|
| 应用 | 请求量、P95/P99、超时率、连接池等待、SQL调用次数 | 慢在业务执行、等连接,还是调用数据库 |
| MySQL | SQL耗时、扫描行数、活跃线程、锁等待、物理读、日志等待 | 慢在执行、读取、提交,还是事务竞争 |
| CPU | 用户态、系统态、I/O等待、运行队列、虚拟化steal时间 | 算力不足,还是CPU在等待其他资源 |
| 内存 | 可用内存、进程RSS、换入换出、缓存占用 | 缓存是否不足,是否发生内存争用 |
| 存储 | 读写IOPS、吞吐、延迟、队列深度 | 是小块随机I/O、带宽限制,还是排队 |
| 网络 | 应用到数据库RTT、重传、流量、连接异常 | SQL本身慢,还是通信和结果传输慢 |
这里的关键是“同时”。拿上午的CPU曲线解释下午的慢日志,或拿整机日均I/O解释一分钟的提交抖动,都容易得到错误结论。
用只读采集建立基线
以下系统命令适用于安装了sysstat工具的Linux环境,可分别在不同终端运行。采样本身也有开销,繁忙生产环境应先使用短窗口,避免持续输出大量诊断数据。

# 块设备延迟、队列、IOPS与吞吐;跳过启动以来的首组平均值
iostat -xz -y 1 60
# CPU、运行队列、内存与换入换出
vmstat 1 60
# 按进程名称匹配mysqld,观察CPU、内存与I/O
pidstat -u -r -d -C mysqld 1 60
# 网卡流量、TCP活动与重传
sar -n DEV,TCP,ETCP 1 60
vmstat第一行通常是系统启动以来的平均值,分析当前变化时应主要看后续采样。容器或受资源配额限制的实例,还需要检查容器内存上限、CPU配额和节流情况,不能仅凭宿主机资源判断数据库余量。
MySQL先确认版本及关键状态:
SELECT VERSION();
SHOW GLOBAL STATUS
WHERE Variable_name IN (
'Threads_connected',
'Threads_running',
'Questions',
'Com_commit',
'Com_rollback',
'Innodb_buffer_pool_read_requests',
'Innodb_buffer_pool_reads',
'Innodb_buffer_pool_pages_dirty',
'Innodb_buffer_pool_pages_total',
'Innodb_data_reads',
'Innodb_data_writes',
'Innodb_data_fsyncs',
'Innodb_log_waits'
);
其中多数计数器是累计值,应间隔采集两次后计算增量;Threads_running、脏页数量等则是采样时的状态值。计数器下降时,应先排查实例重启或统计重置,不能继续按正常增量计算。
二、观察指标联动,而不是给单个数值贴标签
一组模拟曲线如何缩小排查范围
下面是一组用于说明判断过程的模拟数据,不代表具体服务器的性能。三个窗口的请求量接近,查询结构发生变化后,应用延迟逐步上升。

| 指标 | 正常窗口 | 变慢初期 | 持续变慢 |
|---|---|---|---|
| 请求量 | 约500次/秒 | 约510次/秒 | 约495次/秒 |
| 应用P95 | 85毫秒 | 620毫秒 | 1300毫秒 |
| MySQL用户态CPU | 42% | 47% | 49% |
| InnoDB物理读增量 | 45次/秒 | 720次/秒 | 1500次/秒 |
| 数据盘读平均延迟 | 1.5毫秒 | 7毫秒 | 19毫秒 |
| 数据盘平均队列长度 | 0.2 | 6 | 28 |
| 目标SQL每次扫描行数 | 约40行 | 约4万行 | 约4.2万行 |
| 应用到数据库RTT | 约2毫秒 | 约2毫秒 | 约2毫秒 |
| 活跃执行线程 | 8 | 37 | 79 |
这组变化更支持如下因果候选:目标SQL扫描量增加,触碰更多数据页,物理读增加,磁盘队列拉长,SQL完成变慢,最终活跃线程堆积。
但它还不能证明“必须扩大Buffer Pool”。还应排除执行计划变化、统计信息变化、新SQL上线、报表任务并发、冷缓存和其他进程争用磁盘。若扫描行数先上升,优先检查SQL与索引;若扫描量稳定、热点数据扩大后物理读上升,缓存容量才更值得关注。
CPU不高不等于数据库没有瓶颈。线程等待磁盘、锁或日志持久化时,整体延迟可以上升,而CPU仍有明显空闲。
用另一组指标排除替代解释
| 观察到的联动 | 更值得优先验证的原因 | 不应直接下的结论 |
|---|---|---|
| CPU用户态上升,运行队列增长,磁盘延迟平稳 | 大量扫描、排序、表达式计算或并发过高 | 一定要升级磁盘 |
| 可用内存下降,持续换入换出,磁盘读写同步上升 | 内存超配、连接工作区膨胀、同机进程争用 | 一定是数据盘性能不足 |
| 写延迟和队列上升,提交耗时增加 | 日志持久化或写入路径排队 | 调大缓存就能解决 |
| 锁等待增长,长事务存在,CPU和磁盘不忙 | 事务竞争、热点行、元数据锁 | 服务器配置太低 |
| SQL端耗时稳定,应用耗时和TCP重传增加 | 网络、连接池或应用排队 | MySQL执行变慢 |
| 活跃线程增加,完成吞吐不再增长,延迟恶化 | 并发超过当前系统处理能力 | 继续增加连接数 |
CPU统计也要分层看。整机平均利用率不高,仍可能有一个核心持续繁忙;虚拟化环境中steal升高,则需要核对宿主资源竞争。iowait只是CPU处于空闲且存在I/O等待的一种统计表现,并不等于磁盘利用率,也不能单独证明设备饱和。
香港服务器的地域因素主要作用于网络路径,而不是InnoDB参数。应用和数据库同在香港,也不代表公网连接的RTT必然稳定;应用位于其他地区时,多次数据库往返更可能放大网络耗时。地理位置、机房位置与实际通信路径,应分别核实。

三、把慢查询拆成执行、读取、等待和传输
先找“总成本高”的SQL,再看单次最慢的SQL
慢查询分析至少有两个入口:单次耗时高的语句,以及单次不算慢、但调用频繁且累计耗时高的语句。只按最长执行时间排序,容易忽略第二类。
在MySQL 8.0中,可以通过Performance Schema查看语句摘要。下面是只读查询,结果取决于相关采集是否启用:
SELECT
SCHEMA_NAME,
DIGEST_TEXT,
COUNT_STAR,
ROUND(SUM_TIMER_WAIT / 1000000000000, 2) AS total_seconds,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_ms,
SUM_ROWS_EXAMINED,
SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
这些统计通常覆盖累计观察周期,并不是刚刚一分钟的数据。要比较故障窗口,应保存两次快照,按摘要计算调用次数、总耗时和扫描行数的增量,不要为方便观察而随意清空生产统计。
对于目标SQL,重点联动查看:
- 执行次数是否突然增加,是否出现循环查询或重试放大。
- 每次扫描行数是否增加,返回行数是否仍然很少。
- 同样的SQL是否只在特定参数下变慢,例如时间跨度扩大、租户数据量不同。
- 执行计划是否改变,索引是否仍能支持过滤、连接与排序。
- SQL耗时上升时,磁盘读取、CPU或锁等待究竟哪一项同步变化。
EXPLAIN用于检查访问路径和估算行数,但估算值不等于实际执行结果。Using filesort也不必然代表落盘排序,应结合排序规模、内存使用和I/O判断。
MySQL 8.0.18及之后的EXPLAIN ANALYZE会实际执行受支持的语句,不能把它当成无成本诊断。生产环境中的高成本查询,应优先在测试环境或负载可控的只读副本验证,并核对副本数据、统计信息与主库是否具有可比性。
慢日志能定位语句,但不能独立解释原因
若未开启慢日志,临时采集前应记录原来的开关、输出方式、阈值和日志路径,确认磁盘余量、目录权限及日志轮转策略。完成采集后恢复原设置;日志可能包含业务字段和敏感条件,分析与分享前需要脱敏。
核对现有设置可以使用:
SHOW GLOBAL VARIABLES
WHERE Variable_name IN (
'slow_query_log',
'slow_query_log_file',
'log_output',
'long_query_time',
'min_examined_row_limit',
'log_queries_not_using_indexes'
);
繁忙实例不宜直接把阈值降到极低,或不加限制地记录所有未使用索引的语句。后者可能产生大量日志,而且小表全扫描未必是性能问题。动态修改全局long_query_time通常影响新连接的会话默认值,连接池中的既有连接可能仍保留原值,采集时要确认实际生效范围。
慢日志中的扫描行数、返回行数和耗时值得关注,但其中的Lock_time并不覆盖所有InnoDB事务等待。锁竞争应结合performance_schema.data_lock_waits、metadata_locks和事务信息判断;具体视图及字段以运行版本为准。
出现“磁盘不忙、CPU不高、SQL却排队”的情况,尤其要检查长事务、热点更新和DDL引发的元数据锁等待。此时调整I/O参数通常无法解除阻塞。
四、让InnoDB参数对应已确认的瓶颈
Buffer Pool看物理读,不只看命中率
区间内的Buffer Pool逻辑读请求与物理读,可以用于估算命中率:
区间命中率 ≈ 1 − 物理读增量 ÷ 逻辑读请求增量
例如10秒内逻辑读请求增加100万次、物理读增加5000次,命中率约为99.5%。但5000次物理读折算为每秒500次,对存储来说仍可能形成压力。高命中率并不等于物理读可忽略,还要结合每秒物理读、磁盘延迟和SQL访问范围。
innodb_buffer_pool_size适合在以下条件同时成立时考虑扩大:热点数据无法留在缓存中,物理读持续影响响应时间,SQL扫描路径合理,并且实例有可用内存预算。
对于数据库专用、同机进程很少的实例,可以把约60%~70%的有效内存作为初步评估区间,而不是固定推荐值。例如16 GiB有效内存的实例,可先评估约10 GiB的Buffer Pool;仍需为连接工作区、Performance Schema、操作系统和峰值任务留出空间。GiB采用二进制口径,1 GiB等于1024 MiB。
如果数据库运行在8 GiB内存限制的容器中,就应以8 GiB限制为预算,而不是宿主机的内存总量。增加Buffer Pool后若发生持续换页,收益可能被抵消。
写入、刷脏和日志容量要分别判断
| 参数或设置 | 适合关注的条件 | 修改后重点复测 |
|---|---|---|
innodb_buffer_pool_size | 热点缓存不足、物理读影响查询 | 物理读、SQL延迟、可用内存、换页 |
innodb_io_capacity | 后台刷脏能力与持续写入负载不匹配 | 脏页趋势、数据盘写延迟、前台响应 |
innodb_io_capacity_max | 刷脏高峰需要更大的后台I/O空间 | 队列是否积压、查询是否被刷盘挤占 |
innodb_redo_log_capacity | 重做日志空间压力导致频繁检查点推进 | 检查点压力、刷脏节奏、写入延迟 |
innodb_log_buffer_size | 大事务等场景出现日志缓冲等待 | Innodb_log_waits增量、事务规模 |
innodb_flush_log_at_trx_commit | 事务提交等待与持久化策略有关 | 提交延迟及可接受的数据丢失边界 |
sync_binlog | 启用binlog且提交受其同步写影响 | binlog同步等待、复制与恢复要求 |
innodb_flush_method | 文件I/O路径与缓存策略需要调整 | 双重缓存、内存压力、读写延迟 |
innodb_io_capacity及其上限用于指导后台I/O活动,不是磁盘的标称IOPS,也不是所有前台读写的硬限速。数值过低可能使脏页积累,过高则可能让后台刷盘挤占前台请求的存储资源。应根据持续负载下的设备表现逐步调整,而不是照搬短时压测峰值。
MySQL 8.0.30开始使用innodb_redo_log_capacity配置重做日志总容量;更早版本的配置方式不同。扩大日志容量可能缓解检查点压力,却不会消除每次提交的持久化等待,也不是无限扩大就能提升性能。
Innodb_log_waits增加提示存在日志缓冲等待,但不能单独证明磁盘太慢。事务大小、日志生成速度和刷新过程都应纳入分析。大事务或集中批量写入,往往还需要调整事务边界。
对要求可靠事务持久化、且启用binlog的业务,通常保留innodb_flush_log_at_trx_commit=1与sync_binlog=1,同时确认底层存储正确实现持久化语义。降低同步强度属于耐久性取舍,不应作为通用性能优化。
参数调整前先记录版本和原值
SHOW GLOBAL VARIABLES
WHERE Variable_name IN (
'innodb_buffer_pool_size',
'innodb_io_capacity',
'innodb_io_capacity_max',
'innodb_redo_log_capacity',
'innodb_log_file_size',
'innodb_log_files_in_group',
'innodb_log_buffer_size',
'innodb_flush_log_at_trx_commit',
'sync_binlog',
'innodb_flush_method',
'max_connections'
);
版本不支持的变量可能不会出现在结果中。不要因此直接向配置文件添加网上找到的参数。
每次修改都应记录原值、生效范围和是否需要重启,并备份配置文件;涉及耐久性、日志或重启操作时,还应确认数据库备份可恢复。回滚需要同时恢复运行值与持久配置,避免当前恢复、下次启动又重新应用新值。
五、沿存储路径排除“看起来像磁盘慢”的问题
延迟和队列比单看利用率更有解释力
iostat中的await包含请求排队与处理时间。分析读查询时看r_await,分析写入时看w_await,再结合平均队列长度、IOPS和吞吐。
若IOPS增加、延迟保持稳定,设备可能仍能承受当前负载;若IOPS不再增长,但队列和延迟持续上升,则更接近存储处理能力或外部限额瓶颈。NVMe、多队列设备、RAID和虚拟磁盘具有不同并行能力,%util接近100%不能统一解释为已达到性能上限。
还要区分数据文件、redo日志、binlog和临时文件所在的路径。如果它们最终落在同一个后端卷上,逻辑目录不同并没有真正隔离I/O。

在香港服务器环境中,无论使用本地盘还是云盘,都应核实:
- 标称能力是持续值还是突发值,是否有突发额度耗尽的情况。
- 是否同时存在IOPS、吞吐和实例级总I/O限制。
- 数据库是否与备份、日志压缩、报表导出等任务争用同一存储。
- 虚拟机看到的设备队列是否足以反映后端排队。
- 文件系统、挂载方式与MySQL版本的I/O模式是否兼容。
文件I/O模式不能脱离版本和存储栈
在受支持的Linux配置中,innodb_flush_method=O_DIRECT可用于减少InnoDB数据文件与操作系统页缓存之间的双重缓存,但它不是消除所有缓存或同步写开销的开关。是否适用,应结合MySQL版本、文件系统及实际存储路径核验。
不要同时修改刷盘方式、I/O容量、Buffer Pool和连接数,否则复测改善后也无法判断哪个变量起作用。
存储测试也不能替代数据库测试。顺序大块读写吞吐较高,不代表小块随机读和低队列深度同步写延迟同样理想。需要压测时,应在专用测试卷或明确隔离的测试文件上进行;不得覆盖生产数据文件或直接写裸设备。操作前确认目标路径、备份及容量,结束后恢复原配置并仅清理已确认的测试文件。
排除网络和应用造成的外部等待
若数据库内部执行时间稳定,应用端SQL调用耗时却上升,应进一步拆开连接池等待、建立连接、发送请求、执行、返回结果和应用处理时间。
一条查询耗时不高,并不意味着一个接口的数据库时间不高。接口若串行执行数十次SQL,即使每次只多出几毫秒网络往返,也可能形成明显延迟。大结果集还会增加网络传输和应用反序列化成本。
验证网络时,应从实际应用节点观察数据库方向的RTT、TCP重传及连接异常,再与数据库内部耗时对齐。不能用运维电脑到香港服务器的延迟,替代应用到数据库的路径。
六、形成判断后,用同一组指标复测
有效的判断应能说明证据、替代解释和验证方法。例如:
慢查询主要由扫描范围扩大触发。相同请求量下,每次扫描行数先增加,随后物理读、磁盘队列和SQL延迟上升;网络RTT与锁等待未同步恶化。优先验证索引和SQL条件,暂不通过降低事务持久化要求来改善响应。
这比“磁盘利用率高,所以换盘”更具体,也更容易复测。
复测应尽量保持数据规模、查询参数、并发、事务大小和缓存状态一致。冷缓存与热缓存应分别标记;相同SQL在空闲时跑得快,不代表高并发下不会排队。只改一个主要变量,并保留修改前的指标快照。
验收不只看P95下降,还要检查完成吞吐是否提高、超时率是否下降、扫描成本是否减少,以及有没有新增换页、复制延迟或后台刷盘压力。若连接数增加后吞吐没有提升,反而活跃线程与尾延迟上升,应收紧并发,而不是继续提高max_connections。
下一次慢查询出现时,按问题类型同时观察以下组合:
- 读查询变慢: SQL扫描行数、区间物理读、Buffer Pool状态、读延迟、磁盘队列、SQL P95。
- 事务提交变慢: 提交耗时、日志相关等待、同步写延迟、写队列、脏页趋势、事务规模。
- CPU持续繁忙: 单核利用率、运行队列、SQL执行次数、扫描与排序成本、完成吞吐。
- 内存紧张: 可用内存、mysqld RSS、连接并发、工作区占用、换入换出、磁盘延迟。
- 数据库内部不慢但接口慢: 连接池等待、SQL调用次数、应用到数据库RTT、TCP重传、返回数据量。
- 资源不忙但请求堆积: 活跃线程、行锁与元数据锁等待、长事务、应用队列、错误率。
对于香港服务器上的MySQL,这些组合比一组固定的InnoDB参数更有复用价值:先让指标形成同一时间内的证据链,再决定优化SQL、调整缓存、改变刷盘节奏,还是处理存储、网络与应用排队。
围绕香港服务器上的MySQL数据库、业务后台与接口服务,A5数据提供覆盖Xeon Gold、AMD EPYC等平台的物理服务器,配备SSD或NVMe存储及不同内存规格,为缓存、数据文件和多任务运行提供硬件资源基础。香港产品同时提供CN2与国际带宽选项,A5数据还覆盖美国、日本、新加坡等地区,可结合应用访问区域与数据库部署路径配置相应的服务器资源。



