MySQL 8.4 装完先改这一个参数
MySQL 8.4 把一个默认值改成了 O_DIRECT。读写都绕过操作系统的页缓存。而配套的那个参数,默认值两版都没动,还是 128 MB。
装完 MySQL 8.4,服务正常启动。SHOW VARIABLES 一切正常,监控也没什么异常。但读性能可能比 8.0 还差。而且是那种看起来没坏、就是慢的很。
原因不是配置写错了。是 8.4 改了 innodb_flush_method 的默认值,而它牵连的那个参数没跟着改。
好在要改的只有一个参数。这篇讲三件事:为什么是它、该改多少、怎么验证生效。
机器是云服务器,4 核 8G,跟 Web 服务共用一台。数据库是 MySQL 8.4.11,容器镜像 mysql:8.4.11。部署走 1Panel 容器化,端口只映射到 127.0.0.1:3306,不暴露公网。同机还跑着 OpenResty 容器、PHP-FPM 容器和 1Panel 面板。
命令里用变量代替容器名,先在终端执行一次:
# 换成你在 1Panel 里给 MySQL 起的名字(= 容器名)
export MYSQL_CTN="mysql84"
回显来源说明一下。标「实测」的来自这次部署,2026-09-16。原始数值原样保留,只把表格边框排整齐。标「官方」的是 MySQL 官方文档、官方 Bug 系统、Oracle 官方博客的原文,出处随文标注。
为什么是 8.4,不是 8.0
选版本没什么可纠结。MySQL 8.0 的社区版支持已经在 2026 年 4 月 30 日结束。8.4 是 MySQL 的第一个 LTS 版本,官方生命周期排到 2029 年,扩展支持至 2032 年。
8.0 已经不是能用就行的问题。停止支持意味着安全补丁不再下发。新装环境没有理由选它。
但升级到 8.4 有个代价,藏在默认值里。
两个官方事实,拼出变慢的因果链
事实一。8.4 把 innodb_flush_method 的默认值改成了 O_DIRECT。
这条在 MySQL 官方 Bug 系统里有白纸黑字的结论。用户提了 Bug #117691,标题直译过来是「MySQL 8.4 和 9 没有利用 Linux 页缓存,与 8.0 相反」。开发者的官方答复是:
The default value of
innodb_flush_methodis changed toO_DIRECTin 8.4+
to benefit production servers running with large buffer pool. If users want to use a
small buffer pool, they may explicitly configure theinnodb_flush_methodtofsync.
… Thus, this is not a bug but an expected change in behavior.
翻译成一句话。8.4 假设你会给一个大 buffer pool,所以把绕开 OS 缓存设成了默认。官方给出的处方是两条路。要么把 buffer pool 给够,要么显式改回 fsync。
配套的默认值变化还有一批,官方文档和 Percona 的 8.4 默认值对照表均列明。innodb_flush_method 在 Linux 上从 fsync 变成 O_DIRECT,不支持时回落 fsync。innodb_io_capacity 从 200 变成 10000。innodb_log_buffer_size 从 16 MB 变成 64 MB。innodb_adaptive_hash_index 从 ON 变成 OFF。innodb_change_buffering 从 all 变成 none。
8.4 的整体方向是开箱即生产。但有一个关键参数除外。
事实二。innodb_buffer_pool_size 的默认值,两版都是 128 MB。
Oracle 官方博客《Auto Adapting Configuration Parameters in MySQL》原文:
The default values are maintained for
innodb_buffer_pool_sizeat 128 MB
andinnodb_redo_log_capacityat 1 GB in both MySQL 8.0 and MySQL 8.4.
两个事实拼起来:
在 8.0 上,fsync 走 OS 页缓存。即使 buffer pool 给得小,热点数据还能被操作系统的空闲内存兜一层,于是也能跑。
到 8.4,O_DIRECT 把这一层撤了。读数据文件也绕开页缓存,缓存职责全部落回 buffer pool。而此时 buffer pool 还是 128 MB。
兜底的那一层没了,新的那一层没建起来。
这就是升级后反而变慢的完整因果链。它不是 bug。是默认值的组合效应。
这一个参数,该给多少
官方给了一个自动档,这台机器用不了
Oracle 官方博客里提供了一个省事方案。打开 innodb_dedicated_server,让 InnoDB 自己算:
You can now move the MySQL server to production using the default configuration
by simply setting these two variables:max_connections = Nandinnodb_dedicated_server = ON
它的算法,官方原文。innodb_buffer_pool_size:可用物理内存 M 小于 1 GB 时给 128 MB。1 GB 到 4 GB 之间给 M × 0.50。大于 4 GB 时给 M × 0.75。innodb_redo_log_capacity:逻辑 CPU 数除以 2 GB,上限 16 GB。
把 4 核 8G 代进去算一遍。buffer pool 6.00 GB,redo 2.0 GB,合计 8.00 GB。正好等于整机内存。连操作系统都没地方待,更别提同一台机器上还跑着 OpenResty、PHP-FPM 和 1Panel 面板。
另外两种配置也列一下。4 核 16 GB 算出来是 12.00 GB 加 2.0 GB,合计 14.00 GB。8 核 16 GB 是 12.00 GB 加 4.0 GB,合计 16.00 GB。
为什么算得这么激进?因为 innodb_dedicated_server 的语义是这台机器专供 MySQL。dedicated 就是专用的意思。而我们这台是共用机器。
结论是:共用一台机器的场景,这个自动档不能用,必须手动算。
手动算,从整机内存倒着分
算账的顺序是先扣掉别人,剩下的才归 MySQL。
innodb_buffer_pool_size 给 2.0 GB。innodb_redo_log_capacity 给 1.0 GB。MySQL 自身加连接加排序缓冲,估 0.5 GB。OpenResty 加面板加系统,估 1.0 GB。PHP-FPM 按 10 个子进程、每个 60 到 80 MB 算,0.8 GB。
合计 5.3 GB。8 GB 减掉合计,余量 2.7 GB。
留这 2.7 GB 的意义是:流量高峰时 PHP-FPM 子进程会涨,缓冲区、连接数都会涨。这块是安全垫。
所以这台机器给 innodb_buffer_pool_size = 2G。如果你的是 4 核 8G 且只跑 MySQL,可以给到 4G 到 5G。如果是共用,2G 是稳妥的起点。上线后看 innodb_buffer_pool_read_requests 与磁盘读的比例再调。
改法与验证
配置写在 1Panel 的 MySQL 配置文件里。路径是 /opt/1panel/apps/mysql/<应用名>/conf/my.cnf。
[mysqld]
# ---------- 内存(适配 4核8G)----------
innodb_buffer_pool_size = 2G # ← 唯一必须改的一个:8.4 绕过 OS 缓存,靠这里兜住热点数据
innodb_redo_log_capacity = 1G # 8.4 默认值,写出来只是显式记录
innodb_log_buffer_size = 64M # 8.4 默认值(8.0 是 16M),写出来只是显式记录
innodb_flush_method = O_DIRECT # 8.4 默认值,写出来只是显式记录
# ---------- 连接 ----------
max_connections = 200
# ---------- 字符集 ----------
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# ---------- 时区 ----------
default-time-zone = '+08:00'
# ---------- 慢查询 ----------
slow_query_log = ON
long_query_time = 2
注意注释里那句话。上面四项内存参数里,后三项都是 8.4 的默认值。写出来只是为了让配置自解释,改不改行为一样。真正影响性能的只有第一项。
这也是这篇标题的由来。8.4 已经把一批默认值调成生产可用了,你不用逐条照抄 8.0 时代的老教程。唯独 innodb_buffer_pool_size 必须自己给。
改完重启容器,然后验证:
docker restart "$MYSQL_CTN"
# 进容器用 mysql 客户端核验(密码换成你自己的)
docker exec -i "$MYSQL_CTN" mysql -uroot -p'<你的密码>' -e "
SELECT VERSION();
SHOW VARIABLES WHERE Variable_name IN
('innodb_buffer_pool_size', 'innodb_flush_method');
"
回显(2026-09-16 实测):
+-----------+
| VERSION() |
+-----------+
| 8.4.11 |
+-----------+
+-------------------------+------------+
| Variable_name | Value |
+-------------------------+------------+
| innodb_buffer_pool_size | 2147483648 |
| innodb_flush_method | O_DIRECT |
+-------------------------+------------+
2147483648 就是 2 GB,2 × 1024³。参数值不要靠肉眼估,看回显里的字节数。写 2G 得到的是这个数,写 2048M 也是这个数,写 2000M 就不是了。
我当时看 SHOW VARIABLES,一项都没异常。我以为装完就能用,服务也确实起来了。会出问题的地方在读,不在启动这一层。
顺手要处理的,跟性能无关但不改会出事
这几个参数跟性能无关,属于不改迟早出事故的类型。
排序规则。utf8mb4_0900_ai_ci 的陷阱
8.4 的 utf8mb4 默认 collation 是 utf8mb4_0900_ai_ci。而项目里所有 SQL 脚本、表定义、连接串用的都是 utf8mb4_unicode_ci。
两者混用会在 JOIN、比较、子查询时报错:
Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='
建库时必须显式选 utf8mb4_unicode_ci。留空就会拿到 8.4 的默认值。服务端也一并锁死:
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
认证插件。老客户端连不上
8.4 默认禁用了 mysql_native_password,全面转向 caching_sha2_password。这一点官方升级文档里有明确说明。
后果是老版本客户端连不上。比如 Navicat 11 及更早版本就不支持 caching_sha2_password。解决方式是升级客户端,Navicat 16+ 或 DBeaver,而不是把认证插件改回去。
时区与慢查询
default-time-zone = '+08:00'
slow_query_log = ON
long_query_time = 2
时区这条要和 PHP 侧的 date.timezone 对上,本案是 Asia/Shanghai。否则入库时间和取出时间会差 8 小时。这类问题排查起来非常费时。
慢查询日志建议建库时就开。long_query_time = 2 表示超过 2 秒的语句落盘。新站流量小,但它记录的是未来某一天突然变慢的第一手证据。
这台机器上 2G 是起点。到底够不够,得看上线后 innodb_buffer_pool_read_requests 和磁盘读的比例。这个数我还没跑出来,等待后续补充。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)