云数据库MySQL 8.0升级后性能反降?这四个隐形坑位在偷你的并发

先扯个真实场景。前阵子帮一个客户处理线上问题,业务量没涨,配置没动,就是从MySQL 5.7升到8.0后,原本跑得挺稳的库开始周期性抖动,慢查询日志里全是些本该走索引的小查询。查了一圈,不是锁问题,不是慢SQL本身的问题,而是8.0版本偷偷改了好几个默认行为,误导了新手甚至不少老手的调优方向。如果你是刚把业务迁到云数据库MySQL 8.0上,下面这几个坑位务必对照检查。

坑位一:TempTable引擎的磁盘溢出比你想的更早

5.7时代,临时表默认用MEMORY引擎,超过`tmp_table_size`就落盘。8.0换了默认引擎TempTable,内存上限由`temptable_max_ram`控制,默认**1GB**。看起来很美好对吧?但TempTable有一个坑:数据量超过内存上限后,**后续所有临时表操作都会直接落盘**,且落盘路径不是`/tmp`,而是`innodb_temp_tablespaces_dir`指向的InnoDB临时表空间。这意味着临时表空间文件会持续膨胀,且跟你的数据文件抢IO。 更隐蔽的是——如果你的实例内存只有4GB或8GB,而`temptable_max_ram`仍是默认的1GB,那么一个稍大的`ORDER BY`或者`GROUP BY`就可能提前触发磁盘临时表,把IO延迟瞬间拉高。在云数据库这种共享底层存储的环境下,IO抖动是致命的。 排查命令: ```sql -- 查看当前临时表内存上限 SHOW VARIABLES LIKE 'temptable_max_ram'; -- 查看临时表落盘情况 SHOW STATUS LIKE 'Temp_tables%'; -- 查看临时表空间文件大小(Linux shell执行) ls -lh /var/lib/mysql/#innodb_temp/ ``` 如果`Created_tmp_disk_tables`增长明显,建议下调`temptable_max_ram`到512MB或256MB,强制更早判断是否需要改写SQL,而不是让一个大查询慢慢吃掉内存。配合`tmp_table_size`=64MB(8.0中仅对MEMORY引擎生效)一起调,能有效减少磁盘临时表产生。
# 直接生效
SET GLOBAL temptable_max_ram = 536870912;
# 持久化(注意版本差异,8.0.30+支持ALTER INSTANCE方式)
SET PERSIST temptable_max_ram = 536870912;

坑位二:redo log容量缩水,高写入必炸

5.7时代调`innodb_log_file_size`是常规操作,8.0.30之后这个参数被废弃,取而代之的是`innodb_redo_log_capacity`,默认**100MB**。看清楚了,是100MB,不是1GB。在SSD出现之前,redo log设太大的确恢复慢,但现在的云盘IO能力完全不是瓶颈。100MB的redo log意味着什么?就是你的InnoDB几乎每秒钟都要做一次checkpoint,把脏页刷到磁盘。刷盘一多,磁盘IO开销成倍增长。 更坑的是,这个默认值对不同IO能力的实例是“一刀切”。你在低配VPS上跑小业务也许没感觉,但在高IOPS云数据库实例上,业务一波动,`log_wait_for_flush`事件会直接飙升。碰到这个情况,新手第一反应是加大`innodb_buffer_pool_size`,其实是本末倒置——刷脏页的压力源头在redo log容量太短。 排查: ```sql SELECT @@innodb_redo_log_capacity; SHOW ENGINE INNODB STATUS\G -- 查看Log sequence number与Log flushed up to的差值 ``` 调整方式(8.0.30+):
SET PERSIST innodb_redo_log_capacity = 1073741824; -- 1GB
注意:如果实例已经在IO抖动中,直接`SET PERSIST`会触发一次checkpoint,可能导致短暂hang住。建议低峰期操作。这个坑在云数据库MySQL 8.0上尤其常见。我自己之前用的一款云服务就因为没关注这个参数,高峰期写放大严重,后来在轻云互联的云数据库实例上对比测试,才意识到是redo log容量设置导致的差异,调整后吞吐明显稳定了。

坑位三:caching_sha2_password认证插件是连接超时的隐形凶手

MySQL 8.0默认认证插件从`mysql_native_password`换成了`caching_sha2_password`。对于新连接,首次认证需要走RSA加密传输密码,这本身没问题。但很多老应用驱动(尤其是PHP 7.x早期的pdo_mysql、某些版本的Python mysql-connector)不支持新的认证协议,于是DBA们常干一件事: ```sql ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; ``` 改完之后连接倒是通了,但性能呢?注意,`caching_sha2_password`自带一个缓存机制:首次认证会走完整握手 + RSA加密,后续命中缓存的连接直接快速认证。而`mysql_native_password`没有任何缓存,每次连接都是同样的完整握手流程。如果业务用了短连接,且你手动把认证插件改回旧版,每次新建连接的开销会比8.0原生的多2-3个RTT。 更头疼的是,云数据库控制台通常只允许创建账号,不允许直接改`authentication_policy`。如果你在参数组里看到`authentication_policy=mysql_native_password`,意味着所有新建连接都在走旧版认证,高并发短连接场景下,连接建立本身就消耗大量CPU。 排查看这个指标: ```sql SHOW STATUS LIKE 'Aborted_connects'; SHOW STATUS LIKE 'Threads_created'; ``` 如果`Threads_created`很高而`Aborted_connects`正常,说明连接复用没问题;如果`Aborted_connects`也在涨,很可能就是认证协议的握手超时。 靠谱的思路是:优先让客户端驱动升级到支持caching_sha2_password的版本,而不是在服务端降级。如果驱动一时半会升不了,那就把连接池的`maxLifetime`调大,尽量复用连接。轻云互联的云数据库控制台可以直接调整`authentication_policy`,但请记住——降级一时爽,连接池火葬场。

坑位四:buffer pool设置的“七成内存”公式会让你死得很惨

无数入门教程告诉你`innodb_buffer_pool_size`设为内存的70%,这招在物理机上凑合能用,在云数据库实例上往往翻车。因为云数据库实例的“内存规格”是包含操作系统页缓存和MySQL自身开销的,并非全部可用于InnoDB buffer pool。你设了70%,8GB实例就是5.6GB,看起来合理,但别忘了还有这些吃内存的地方: - `performance_schema`默认开启,占用内存约为buffer pool的1%-2%(每张表、每个索引都要在内存里建监控项) - `innodb_additional_mem_pool_size`虽然没了,但`data dictionary`在8.0改成内存表结构,大实例轻松吃几百MB - 连接数*`thread_stack`+`sort_buffer_size`+`join_buffer_size`+`read_buffer_size`,按100连接算,轻松吃掉1-2GB 所以8GB实例设5.6GB buffer pool,还没开始跑业务,Swap就要被吃满。正确姿势是看实际的内存分配: ```bash free -m cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|SwapTotal' ``` 计算可用内存 = `MemAvailable`(不是MemTotal)。然后在这个基础上减去1GB安全余量,再减连接相关的内存开销,剩下的才是buffer pool的候选值。配合MySQL自身的统计: ```sql SELECT @@innodb_buffer_pool_size/1024/1024 AS 'BufferPool(MB)'; SHOW ENGINE INNODB STATUS\G -- 看Buffer pool hit rate ``` 更聪明的方法是看`performance_schema`的内存占用: ```sql SELECT * FROM sys.memory_by_thread_by_current_bytes; ``` 如果命中率持续>99%,说明buffer pool足够用了,没必要时不时调大。很多新手把慢查询归咎于buffer pool太小,其实查一下内存分配就没这回事了。

最后:以上四个坑位,排完序其实有优先级

先看redo log容量(90%的8.0实例会踩),再看认证插件(连接频繁被杀的特别注意),然后检查TempTable,最后才去调buffer pool。这个顺序也是从“影响面最大”到“影响面较小”的排列。 云计算厂商给的默认参数其实是为“跑起来不死”设计的,不是为“跑得快”设计的。你在控制台看到的那些参数组模板,要么是保守值,要么是为特定场景调过的,别指望开箱即用。真正想让MySQL 8.0在云上发挥性能,光copy之前5.7的配置是不行的,必须理解这些行为变化再动手。