MySQL 的 IO 性能受多种因素影响,包括硬件存储介质类型、软件层的数据库内核架构,以及具体 SQL 语句读取或修改的数据量。
InnoDB 利用操作系统提供的异步 IO 接口,实现了一个用于异步读写数据页、日志和 undo 日志的 IO 子系统。当 SQL 查询请求的数据页不在缓冲池中时,需要进行物理 IO 操作,从底层存储读取或写入数据。
对于读取数据页,InnoDB IO 系统采用同步 IO。同步 IO 调用底层的读取接口进行数据读取。对于写入数据页,InnoDB IO 系统采用异步 IO;例如,后台线程异步将脏页刷新到存储。
除了对普通数据文件的读写 IO 操作外,写入 redo/undo 日志、写入 binlog 日志、排序临时表以及 DDL 重建表空间等操作也会产生大量 IO。
为了优化磁盘 IO,通常可以通过增加缓冲池大小、调整刷新方式、配置操作系统的刷新阈值,以及使用 MySQL 8.0 提供的 fdatasync 替代 fsync 等方式进行优化。具体优化方法请参考 MySQL 5.7 官方文档 或 MySQL 8.0 官方文档 中的优化章节。
建议使用适合高 IOPS(每秒输入输出操作次数)的固态硬盘,这可以有效缓解高 IO 问题。
innodb_log_file_size 控制 InnoDB redo 日志文件的大小。如果该参数值过小,会导致日志文件频繁切换和刷新,增加 IO 操作次数,影响写入性能。适当增大该参数可以减少日志文件切换频率,将多次日志缓冲区刷新合并为一次磁盘 IO 操作,从而提升写入性能。
此外,建议降低读写频率或优化与脏页刷新相关的参数,以解决高 IO 问题。与脏页刷新相关的参数如下表所示。
| 参数名称 | 说明 |
|---|---|
| innodb_max_dirty_pages_pct | 控制 InnoDB 中脏页的最大比例。值越大,允许的脏页越多,但也会增加脏数据刷新的频率和时间。 |
| innodb_max_dirty_pages_pct_lwm | 控制达到脏页最大比例后,InnoDB 开始刷新脏数据的阈值。值越小,InnoDB 越早开始刷新脏数据,但也会增加刷新频率和时间。 |
| innodb_io_capacity | 控制 InnoDB 在磁盘上能够处理的 IO 吞吐量。值越大,InnoDB 对磁盘的压力越大,但也能加快脏数据刷新速度。 |
| innodb_io_capacity_max | 控制 InnoDB 在磁盘上能够处理的最大 IO 吞吐量,决定脏数据刷新速度的上限。 |
| innodb_lru_scan_depth | 控制 InnoDB 在刷新脏页时扫描缓冲池的深度,从而影响高吞吐写入环境下的 IO 负载。值过大时,每次刷新周期需要扫描更多页,导致更多无效 IO 读取,增加整体 IO 负载并影响性能。值过小时,可能只允许 InnoDB 扫描和清理极少量脏页,导致脏页无限累积,产生严重 IO 竞争,加剧 IO 负载。 |
进入 MySQL 容器后,可以使用以下命令查看临时文件目录大小。
如果临时目录较大,可能是由于 SQL 排序或去重较慢,导致生成了较大的临时表。此时需要优化慢 SQL 查询,减少临时表的使用,从而避免临时表写入带来的 IO 增加。
可以通过以下 SQL 命令查询缓冲命中率。
当 SQL 查询或修改的数据不在内存缓冲池中时,必须从磁盘读取。如果读取数据量非常大,可能导致磁盘读写吞吐量非常高。为避免这种情况,应尽量减少全表扫描,例如避免使用 select * from large_table 之类的语句,以减少对缓冲池的污染。
针对具体业务场景,可以考虑重新设计缓存策略或升级实例规格以提升系统性能。
执行 DDL 语句时,可能会发生重建表空间、全表数据扫描、创建索引排序等操作。新表产生的脏页需要刷新,导致大量 IO 吞吐。此外,删除大表时,drop table 操作可能引起 IO 波动。为避免影响业务,应将这些操作安排在业务低峰期执行。
建议将大事务拆分为多个小事务,避免写入大量 binlog 文件,从而降低 IO 吞吐量。例如,删除大量行的 delete SQL 语句可以拆分为多个较小的 delete 语句执行。这样可以减小每个事务产生的 binlog 文件大小,降低磁盘刷新频率,提高性能。此外,优化业务逻辑也有助于避免大事务的产生。