MySQL 8.0 基础优化实战指南:从配置到性能调优
很多人以为装好MySQL 8.0、把数据导进去,数据库就算"能用"了。直到某天业务慢得像蜗牛爬,开发跑过来喊了一句"数据库卡了",你才发现默认配置和真实负载之间隔着一整座山。这篇指南就是要把MySQL 8.0从配置文件、索引设计、SQL写法、锁与事务、监控巡检这五条线串起来,讲清楚优化时先动哪里、后动哪里,以及每个操作背后的原因。不管你是正在接手一个MySQL 8.0项目的开发者,还是已经踩过几个坑但想系统梳理一遍的运维同学,按这个顺序走一遍,基本能把数据库从"跑得动"推到"跑得快"。
我这些年处理过的MySQL性能问题里,至少有一半以上不是机器不行,而是配置没跟业务对齐、索引没建对、SQL写法太随性。所以这篇指南不给那种"装完点下一步"的流水账,只讲真正影响性能的关键动作。
1. 基础配置优化:先别急着动参数,先搞懂这几个关键项
MySQL 8.0安装完用的是一套非常保守的默认配置,目的只是保证它能跑起来、兼容性最好,生产环境的压力和这套配置完全不匹配。举一个最常见的例子:innodb_buffer_pool_size默认只有128M,假如你机器有32G内存,不把这个参数调大,InnoDB的缓存池只够装十分之一不到的热点数据,剩下的请求全部落到磁盘I/O上。磁盘再快,也远不如内存快,性能自然上不去。
所以我调优的第一件事,永远是结合业务特点把基础参数先立起来,然后再谈索引和SQL。顺序反了的话,你后面再努力也可能是在给错误的配置环境打补丁。
1.1 连接数别乱调大,要点是算峰值并发
max_connections默认是151,开发环境甚至小网站都够用,但一到活动大促或定时任务并发场景就特别容易打满。打满之后的表现很典型:应用日志里全是"Too many connections",新请求进不来,老请求又堵着不结束,整个链路雪崩。最简单的确认方式是执行下面这条SQL,看当前连接数和配置上限的差距:
SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';调优时你可能会想"那直接把max_connections设成2000不就完了?"还真不行。每个连接都会占用线程和内存资源,连接数设得太大,CPU会大量浪费在线程切换上,请求响应反而更慢。一般做法是先估算业务峰值时的并发连接数,然后在这个基础上预留30%-40%的余量。比如你用压力测试得出500并发已经能扛住业务峰值,那么max_connections设在800左右就差不多了。
另外,程序侧连接池的设置也很关键。很多应用框架默认的连接池是20到50个连接,一个实例不够就多开几个实例,这比在数据库端放开几千个连接要健康得多。我实测过把同样请求压力从连接池200个连接改成数据库2000个连接,连接池方案下的平均延迟小了一个数量级。别把数据库当成无状态的服务去猛连,连接是个昂贵的资源。
1.2 InnoDB缓冲池与日志文件:性能地基
innodb_buffer_pool_size是MySQL里最重要的性能参数,没有之一。InnoDB会把数据页、索引页、变更缓冲等都放在这个内存区域里,所有读写操作先跟这块内存打交道,内存不够了才落盘。经验上,这个值可以设置到物理内存的60%到75%。举个例子:32G内存的机器,可以给到20G左右;64G内存给到40G到48G。如果你的服务器上不只跑MySQL,还要跑应用进程,那得按实际可用内存重新算,不要把机器搞到开始用swap。
MySQL 8.0支持动态调整缓冲池大小,方便先看效果再固化到配置文件:
SET GLOBAL innodb_buffer_pool_size = 20 * 1024 * 1024 * 1024;但注意,这个动态设置在重启后会丢失,调整到满意的值之后,记得同步修改my.cnf。缓冲池有没有起作用,看命中率:SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';命中的比例越高,说明内存缓存越有效。
再来看innodb_log_file_size。MySQL 8.0默认是48M,这个值对写密集的业务来说实在太小了。redo log太小意味着日志切换特别频繁,每一次切换都要做一次checkpoint把脏页刷到磁盘,磁盘持续高负载,整个数据库的写性能就会明显下滑。在写多读少的业务里,我习惯把innodb_log_file_size调整到512M甚至1G,同时也要观察磁盘空间和恢复时间,日志太大崩溃恢复时花费的时间也会更长。正常运维周期内,1G这个档位对很多中等规模业务都算合理。
innodb_flush_log_at_trx_commit这个参数建议你有意识地选一次。默认值1表示每次事务提交都把日志刷到磁盘,最安全,但每次提交都要等一次磁盘fsync,性能开销大;改成2表示每次提交只写入操作系统缓存,每秒再刷一次盘,性能明显好,但MySQL进程崩溃时可能丢最多1秒的事务。如果是金融交易类业务,老老实实用1;大多数互联网业务能接受极小概率的秒级丢失,可以用2换性能。很多团队完全没有考虑过这个参数,其实它和你的可靠性要求是直接相关的。
1.3 字符集、时区与几个会话级参数的坑
MySQL 8.0默认字符集已经是utf8mb4,这比5.7时代默认latin1强很多,但建库时还是要显式确认一遍。尤其要注意默认的排序规则utf8mb4_0900_ai_ci是不区分大小写的,如果业务要求用户名、订单号查询时必须区分大小写,这个默认值会让你查出来的结果比预期更多,而且还会让LIKE查询带上额外的排序规则转换成本。建表时按实际需要指定COLLATE utf8mb4_bin或者utf8mb4_0900_as_cs,比事后改表省心得多。
时区这个坑也很隐蔽。MySQL 8.0的系统时区和应用服务器的时区如果不一致,你存进去的DATETIME看着没问题,但用JDBC连接时如果服务端时区跟客户端不一致,转换出来的时间完全对不上。排查问题到最后发现是时区偏移引起的,既浪费精力又容易误伤业务逻辑。我习惯在连接串里显式指定时区,同时在MySQL侧把全局时区设为业务统一时区,两边对死。
会话级参数里最容易被高估的是sort_buffer_size。它表示每个连接做排序操作时能使用的内存,看上去大一点排序更快,但这是每个连接都会分配的,如果全局设置了8M,200个连接同时排序就是1.6G内存,内存瞬间被打爆。正确做法是保持默认或小幅调整,然后用状态变量Sort_merge_passes观察实际排序过程中是否有大量落盘操作,如果这个计数很大,再针对性放大排序缓冲。同理还有join_buffer_size,别听网上那些"调大排序缓冲提升性能"的配方就乱改,会话级参数影响范围大,要谨慎。
提示:调参数最忌讳一次性改十几个再重启观察,出了问题根本不知道是哪个参数造成的。一次只改一两个,每次改完做一轮对比压测,或者看一段时间的运行曲线,确认有效再改下一批。
2. 索引优化:慢查询的根源大多在索引设计
配置是地基,但真正让查询变慢的常见原因还是索引没建好。MySQL 8.0的优化器能力比老版本强了不少,但它再聪明也需要一个合理设计的索引结构。很多人遇到慢查询第一反应是"机器性能不行"或者"SQL写得烂",但EXPLAIN一看,全表扫描、没有可用索引、回表次数爆炸,问题清清楚楚。
这一章我会从最典型的索引失效场景讲到执行计划怎么看,最后说联合索引的实用设计原则。看懂这三块,你排查慢SQL的思路会清晰很多。
2.1 最常见的索引失效场景,你大概率踩过几个
先看一个典型例子:在created_at字段上建了索引,查询条件是WHERE DATE(created_at) = '2025-01-01'。对索引列使用函数以后,优化器没法直接利用B+树的有序性做查找,只能把索引列全部取出来算一遍函数再过滤,索引就白建了。正确做法是改成范围查询:WHERE created_at >= '2025-01-01 00:00:00' AND created_at < '2025-01-02 00:00:00'。
隐式类型转换也是重灾区。假如user_id字段是varchar类型,但传入的是数字:WHERE user_id = 123456,MySQL会先把索引列转成数字再比较,同样导致索引失效。判断方法很简单:看看表结构里这个字段到底是什么类型,然后确保传入值跟字段类型严格一致。如果程序里传参类型不统一,建议在SQL层做好类型转换,让参数先变成字符串再进条件。
前置通配符的LIKE查询也是很常见的一个问题。LIKE '%keyword'这种写法,优化器没有办法利用B+树顺序扫描的特性,只能全索引扫描。业务上确实需要模糊查询时,我一般先评估数据量:几千行的小表全表扫也就几毫秒,不用太纠结;但百万行以上就得考虑全文索引或者改造业务设计,让查询条件尽量变成前缀匹配。
最后提一下OR条件。WHERE a = 1 OR b = 2如果只在a上建了索引,b没有索引,优化器很可能放弃索引直接全表扫描。MySQL 8.0虽然支持索引合并,但没有索引的列始终是短板。要么给b也建索引,要么把OR改写成UNION两条独立查询,实测下来改写后的执行计划往往更稳定。
2.2 学会看EXPLAIN,优化器想什么别猜
EXPLAIN是排查索引问题时需要最先学会看的工具,但很多人在MySQL 8.0里还是只盯着一列看,实际上有几个列的信息量很大。
type列反应了访问方式,从好到差大致是:system>const>eq_ref>ref>range>index>ALL。看到ALL基本意味着全表扫描,是优化的头号目标;index虽然也是全索引扫描,但至少比表扫描好一点,也可能是因为查询需要的列都在索引里。实用的判断标准是:能用range及以上级别就合格,出现ALL就要处理。
rows列是优化器预估需要扫描的行数,虽不是真实值,但能看到大概量级,优化前后这个数字的变化能反映出索引是否生效。Extra列里重点留意Using filesort和Using temporary。出现Using filesort说明排序没有用到索引顺序,需要额外排序操作;出现Using temporary说明查询过程创建了临时表,常见于GROUP BY、DISTINCT或者复杂的子查询场景,数据量大时性能下降非常明显。如果看到的是Using index,那说明查询只访问索引就能拿到全部需要的数据,这就是最理想的覆盖索引状态。
MySQL 8.0.18版本开始有EXPLAIN ANALYZE,可以直接输出真实的执行耗时、扫描行数和各步骤耗时占比。用法很简单:EXPLAIN ANALYZE SELECT ...;结果比传统EXPLAIN更直观,连 "实际执行了几次循环、每次耗时多少" 都给了。我在排查慢SQL时,会先跑EXPLAIN看结构,再跑EXPLAIN ANALYZE看真实瓶颈,两步配合基本能把问题定位清楚。
2.3 联合索引与覆盖索引的组合拳
联合索引设计的最核心原则是最左前缀。比如在(status, category_id, created_at)上建了联合索引,查询条件里必须有status才能用上这个索引;只查category_id或者只查created_at是不会命中这个联合索引的。所以设计联合索引时,要把等值查询的列放前面,范围查询的列放后面。
这里有一个比较容易忽略的细节:联合索引遇到范围查询,后面的列就失效了。比如WHERE status = 1 AND category_id > 10 AND created_at > '2025-01-01',在(status, category_id, created_at)索引上,category_id一旦用了范围条件,created_at就没法用索引继续过滤了。所以正确做法是把范围条件涉及的列尽量放到联合索引的最后。如果几个条件都是范围,那就只能取舍,选择区分度最高、过滤效果最好的列放索引里。
覆盖索引是另一个提升效率的大杀器。简单说,就是让查询需要的所有列都包含在某一个索引中,查询时不用回表取数据行。举一个常见的优化案例:业务请求是SELECT id, name FROM user WHERE status = 1,如果只在status上建了普通索引,那么MySQL查到所有status=1的主键后,还得回表读取整行数据再取出id和name;但如果建的是(status, id, name)联合索引,数据在索引里就能全部拿到,省掉了回表这趟I/O。对高并发读多写少的场景,覆盖索引带来的提升非常明显。
索引也不是越多越好。每多一个索引,写入时就要多维护一份B+树结构,更新频繁的表索引太多反而拖累写性能,还占用额外磁盘空间。我的取舍习惯是:每个表的核心查询路径最多设计两到三个联合索引,优先满足最重要的业务查询;次要查询如果数据量不大,走全表扫描也不是不能接受。总是拿所有可能的查询条件来建索引,最终会把表逼到"写一次要更新十个索引"的境地。
3. SQL优化实战:先开慢查询日志,再精准打击
配置和索引都检查过了,接下来要做的是把真正耗时的SQL找出来。这一步靠猜是猜不出来的,必须依赖慢查询日志和性能指标。很多团队的MySQL默认没开慢查询日志,等于飞行员不看仪表盘在盲飞。开了日志你就知道哪些SQL在消耗大量时间,然后再逐条分析、改写。
3.1 慢查询日志的正确打开方式
MySQL 8.0里可以用下面几条命令把慢查询日志打开,同时设置阈值时间。默认阈值是10秒,对绝大多数在线业务来说太宽松了,很多5秒、3秒的SQL可能天天在跑你都不知道。我一般直接设到1秒或者0.5秒,先跑一段时间把高频慢SQL暴露出来再说。
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; SET GLOBAL log_queries_not_using_indexes = ON;有一个非常实用但容易忽略的参数是log_queries_not_using_indexes,它会把没有正确使用索引的SQL也记进慢查询日志,即使执行时间没超过阈值。这对早期发现"还没变慢、但全表扫描量大"的隐患很有帮助。不过生产环境不要长期开着这个选项,因为很多小表查询本来就不需要索引,开着会导致日志量爆炸。我的习惯是:开展优化工作前的1到2周开启,收集完问题SQL就关掉。
拿到慢查询日志以后,先按执行次数和平均耗时排序,找出高频率慢SQL。这里要区分两种问题:一种是单个SQL执行时间特别长,比如好几秒;另一种是每个SQL执行时间几十毫秒,但每秒被调用成百上千次。前者靠优化单条SQL解决,后者往往要上升到应用层加缓存或者限流去治理,单靠数据库层面调优很快会达到上限。
3.2 深翻页与子查询的典型优化套路
分页查询在业务里太常见了,但深翻页问题直到今天依然在大量项目里存在。LIMIT 100000, 20这种写法,数据库不是只取20条,而是扫描完前面的100000条再丢掉,扫描成本高得离谱。我一个朋友接手过一个千万级数据的列表页,翻到第100页之后接口就超时,典型的深翻页问题。
优化方案之一是延迟关联:先通过覆盖索引查出目标主键,再用主键回表取完整数据。改造前是SELECT * FROM orders ORDER BY id LIMIT 100000, 20,改造后类似下面这样:
SELECT o.* FROM orders o INNER JOIN ( SELECT id FROM orders ORDER BY id LIMIT 100000, 20 ) t ON o.id = t.id;这个写法的思路是先让子查询用主键索引快速扫描出这一页需要的20个主键,然后再用连接去取完整数据,避免了整个大表层面的扫描和回表。如果业务上有更明确的排序条件,比如按created_at排序,可以考虑基于游标的方式:记录上一页最后一条的created_at和id,下一页用条件过滤来拿数据,这种写法在千万级数据下可以做到翻页成本恒定。
子查询方面,MySQL 8.0已经从5.7时代的"大部分需要搞成JOIN"进化到会用物化或者半连接策略来优化,但这不是说子查询可以随便写。我的建议是:业务逻辑简单、数据量小的场景,子查询和JOIN差别不大,按可读性选;数据量一大,比如外层几万条、内层扫描全表,就要实际压测验证。一个常用的排查方式是EXPLAIN ANALYZE看执行计划里子查询被物化了几次、扫描了多少行,如果出现逐行循环执行,那就要改写。
3.3 统计数据、直方图与优化器提示
MySQL优化器选择执行计划时依赖表的统计信息,如果统计信息严重过期,优化器可能选择一个明明有可用索引但是代价估算错误而放弃索引的劣质计划。这种情况有个很直观的表现:EXPLAIN里type是ALL,但你明明刚刚才确认索引是存在的。解决办法是执行ANALYZE TABLE 表名;刷新统计信息,再重新看EXPLAIN。
MySQL 8.0新增了直方图功能,对数据分布严重不均匀的列非常实用。比如status字段99%的行是0、1%的行是1,统计信息里可能只记录"大概平均分布",优化器如果因此选择全表扫描去查那个只占1%的status=1,性能就很亏。给这类列建立直方图之后,优化器能看到更真实的数据分布,做出更合理的判断。创建直方图可以用ANALYZE TABLE 表名 UPDATE HISTOGRAM ON status;,维护成本不高,可以针对高频查询涉及的列做。
至于优化器提示如USE INDEX和FORCE INDEX,我的态度比较谨慎。它们在一些紧急上线、临时救火的时候确实有效,能强制让执行计划走你想走的索引。但如果把这种提示固化在业务代码里,将来表结构调整、索引重命名或者数据量变大,强制提示反而会阻止优化器选出更优计划。除非有明确证据表明优化器在可预见的未来都会选错,否则我会优先让优化器自主决策,同时把统计信息维护好。
注意:排查慢SQL别只盯着执行时间一条,还要看平均扫描行数和返回行数的比例。扫描100万行返回10行,说明过滤条件没用好,这才是索引设计应该优化的点。
4. 锁与事务:并发高时最隐蔽的性能杀手
索引和SQL优化好了之后,单条查询本身的速度可能已经很可观了,但只要并发一上来,数据库就可能卡在另一个地方:锁等待和事务冲突。MySQL 8.0的InnoDB默认使用行级锁,但行级锁并不是说就不会锁冲突了。长事务、间隙锁、写写冲突、死锁,任何一个处理不好,都会让整个系统的吞吐量直线下降。
这一章的很多问题在日常压测中不轻易暴露,往往人要等到线上并发真的高了才遇到。所以提前把排查路径和应对手段整理出来,能帮你少走很多弯路。
4.1 锁等待排查:先找到谁锁了谁
最常见的报错是ERROR 1205: Lock wait timeout exceeded; try restarting transaction。遇到这个错误,很多人的第一反应是"我把锁等待时间调大一点"。这个思路只能暂时缓解症状,因为如果锁没释放,你等再久也一样超时,还会让大量请求堆积在锁等待队列里,把连接池打满。
正确做法是先查清楚谁持有锁、谁在等待。MySQL 8.0的performance_schema和相关视图提供了丰富的信息,你可以直接查sys.innodb_lock_waits视图,它类似于一张"锁冲突快照",包含了阻塞事务、被阻塞事务和对应的SQL内容。示例查询:
SELECT * FROM sys.innodb_lock_waits\G如果嫌这个视图信息太细,也可以查information_schema.innodb_trx看当前所有事务的运行时间、状态和SQL,然后结合innodb_lock_waits找到阻塞链。定位到持有锁的事务之后,评估这个事务是否能尽快结束。如果是长事务误锁了大量行,要从业务代码层面找到它为什么迟迟不提交,是逻辑里夹了慢查询,还是事务忘记提交。如果持锁事务是合理的,那就得考虑调整事务执行顺序,让短事务优先拿到锁。
innodb_lock_wait_timeout这个参数我建议线上主动调小一点,默认50秒太长。锁等待超过5到10秒的业务大概率已经积压到用户能感知的程度了,不如让请求快速失败,让应用层自动重试,比所有请求阻塞半分钟最后一起报错健康得多。我见过的稳定业务里,这个值大多设在5到10秒之间。
4.2 死锁分析:不是bug,但必须管
死锁出现的经典场景是两个事务都以不同的顺序加锁。比如事务A先锁了订单表再锁用户表,事务B先锁了用户表再锁订单表,两边互相等对方释放锁,就死锁了。MySQL检测到死锁后会自动回滚其中一个事务,释放资源,并在客户端报ERROR 1213: Deadlock found的错误。
死锁发生后最重要的是拿到现场信息。执行SHOW ENGINE INNODB STATUS;输出内容里找到LATEST DETECTED DEADLOCK那一大段,里面会记录死锁涉及的两个事务分别执行了什么SQL、持有和等待哪些索引记录。分析这段信息,通常能直接看到加锁顺序的矛盾点。
应对手段有几个方向:一是让所有相关事务统一加锁顺序,比如始终先操作订单表再操作用户表;二是缩小事务范围,事务里只保留必要的SQL,减少持有锁的时间;三是在业务层对死锁错误码做重试处理。死锁在并发系统里不可能完全消除,我见过很多团队把报错日志当成事故来排查,其实更重要的是把重试机制做好,让偶发死锁对业务透明。
4.3 隔离级别与长事务监控
InnoDB默认隔离级别是REPEATABLE READ,这个级别为了处理幻读引入了Next-Key Lock,也就是不仅锁住某一行,还会锁住一个范围。并发插入、更新时,间隙锁带来的冲突要比想象的更多。如果业务对数据一致性要求没那么严苛,比如很多报表类、读多写少类的场景,把隔离级别改成READ COMMITTED可以明显减少锁冲突。好处是只有真正匹配的行会被锁住,但要注意副作用:如果业务依赖可重复读的快照一致性,切换之前必须做充分评估。
长事务是另一个容易被忽视的问题。一个事务跑了几分钟甚至几小时,它的undo log会不断膨胀,形成大量历史版本,直接影响SELECT的多版本读性能。同时长事务还会一直持有锁,挡住其他事务。排查方式很简单:
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id FROM information_schema.innodb_trx;重点看trx_started。如果发现某个事务启动时间已经超过你设定的阈值,比如5分钟以上,基本可以认定它是异常长事务。再顺着trx_mysql_thread_id找到对应连接,借助performance_schema查到它正在执行的SQL,就能定位到业务代码里的问题。上线前把长事务监控配置好,比事后人工翻日志要省心太多。
5. 性能监控与日常巡检:别等问题爆发才动手
优化做到这里,配置、索引、SQL、锁都过了一遍,性能多半已经很健康了。但数据库是动态系统,业务量在涨,数据量在涨,访问模式也会变。今天健康的索引,三个月后可能因为新业务查询就变了。所以最后一件事是把监控和巡检机制建立起来,让问题在发酵之前就能被发现。
5.1 要盯的指标和开源监控组合
监控不需要一开始就追求大而全,先盯住跟性能强相关的几个核心指标就够。我日常会固定关注以下几项:
- QPS/TPS:数据库整体负载水平,趋势异常上升或下降都要有数。
- 慢查询数量:慢查询日志条数突增往往意味着某个索引失效或者某条SQL被调用量变大。
- Threads_connected:连接数是否长期高位运行,接近max_connections时就要预警。
- Buffer Pool命中率:长期低于99%要考虑调大缓冲池或优化热点查询。
- 临时表与临时文件:说明查询中出现大量排序或分组,需要回到SQL优化层面处理。
- 锁等待次数与等待时间:判断并发事务冲突是否在恶化。
监控实现方面,常见的开源监控方案是配合一个MySQL导出器采集状态变量,再用时序数据库存指标、用图形面板展示。这套组合的优势在于社区成熟、指标覆盖广,而且不用额外购买商业产品。如果团队规模小,只想快速看个概览,MySQL 8.0自带的performance_schema和sys库里已经有非常丰富的等待事件和诊断信息,不需要额外装东西。
5.2 基准测试与容量规划:让数据说话
优化到底有没有效果,不能靠感觉说"好像快了一点",要用数据说话。压测工具上我经常用的是sysbench,它可以模拟OLTP读写混合负载,在优化前后分别跑一轮,对比QPS和平均延迟。操作流程基本是几步:先准备测试表和测试数据,然后分别执行只读、只写、读写混合的压力测试,记录结果。压测时要注意数据量不能只是几百条,至少上百万行才有参考意义,否则索引、缓冲池等机制根本来不及发挥作用。
容量规划这块也能从压测里找到线索。假设单机压测出来能扛1500 QPS,业务高峰期峰值是800 QPS,那当前余量充足;如果已经到1200 QPS了,就该提前准备扩容方案或者优化热点。压测结果建议记录成表格,每次调整完配置或索引都重新测一轮,长期下来就知道系统的性能拐点在哪里了。把历史压测数据保存下来,对后续做资源预算和新版本评估都很有价值。
5.3 我自己的巡检清单:每周一查,风险前置
最后分享一份我的每周巡检习惯,几分钟能跑完,但对稳定性的帮助非常大。我会固定检查几项内容:
一是配置参数和实际使用的偏差。线上机器跑了几个月,当时设的innodb_buffer_pool_size可能已经不够了,但没人注意到。用状态变量对比一下内存和磁盘的使用趋势,就能发现该扩容的扩容、该调参的调参。
二是慢查询日志的趋势。对比这周和上周同期的慢查询数量,如果稳步上升,赶紧看新增的慢SQL是什么;如果突然下降,也要想想是不是业务量下降了或者高峰错开了。没有任何波动的慢查询量反而可能是日志轮转没生效。
三是锁等待和死锁计数。查SHOW GLOBAL STATUS LIKE 'Innodb_row_lock%';和SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';,这些计数一直往上涨说明并发冲突在增加,需要翻一眼最近的锁等待视图。
四是磁盘空间和binlog大小。binlog增长过快通常意味着有大批量更新或者长时间未处理的事务,磁盘如果被binlog塞满,MySQL会直接停止写入,这比慢查询更致命。
五是缓冲池的命中率和脏页刷新情况。命中率低可以继续调大缓存;脏页比例长期高说明写入能力强但刷盘能力不足,有可能I/O子系统已经达到瓶颈。
这一套巡检做下来,大多数问题都能在变成事故之前被拦截住。优化MySQL 8.0不是一次性工作,它更像一个跟业务共同演进的循环:压测、监控、发现问题、再优化,循环往复。
最后再分享一个心得:遇到性能问题,我总会先看索引和SQL,再看配置,最后才怀疑硬件。很多看似"配置不够"的场景,其实是索引设计不合理导致的假象。你在配置上花再多精力,也不如先把最耗时的那几条SQL拎出来分析一遍。MySQL 8.0本身的优化器已经够强了,我们做优化的主要工作就是给它提供正确的索引和干净的数据环境,剩下的,它会自己搞定。