MySQL 8.0多实例需显式绑定CPU与NUMA节点才能高效利用多核,否则因线程抢占、跨节点内存访问和I/O竞争导致性能下降;须用numactl启动裸mysqld进程,并为各实例独立配置innodb_buffer_pool_instances等参数。

MySQL 8.0 多实例本身不自动“利用多核”,真正起作用的是每个实例独立调度、配合合理参数配置,才能把多核资源吃满。盲目开多个实例反而会因上下文切换和内存争抢导致性能下降。
为什么不能直接用 mysqld_safe 启动多个实例而不隔离资源
默认 mysqld_safe 不做资源绑定,所有实例共享同一套系统调度策略,容易出现以下问题:
- 多个实例的
innodb_read_io_threads和innodb_write_io_threads共同抢占 CPU 时间片,线程数总和超过物理核心数时,上下文切换开销剧增 - 未设置
numa_interleave或未用numactl启动时,实例可能被调度到远端 NUMA 节点,内存访问延迟翻倍 - 所有实例共用同一
tmpdir或innodb_temp_data_file_path,磁盘 I/O 竞争严重,iostat -x 1可见 %util 持续 100% 但吞吐不上升
必须为每个实例显式控制 CPU 绑定和内存域归属。
如何用 numactl + mysqld 启动单实例并绑定指定 CPU 核与 NUMA 节点
假设服务器为双路 16 核(共 32 逻辑核),NUMA node 0 对应 CPU 0–15,node 1 对应 CPU 16–31。要让 instance1(3307)独占 node 0 的前 4 核:
numactl --cpunodebind=0 --membind=0 \
mysqld --defaults-file=/data/3307/etc/my.cnf \
--innodb_read_io_threads=4 \
--innodb_write_io_threads=4 \
--thread_handling=one-thread-per-connection &
关键点:
-
--cpunodebind=0强制进程只在 node 0 的 CPU 上运行 -
--membind=0确保所有内存分配来自 node 0 的本地内存 - 线程数设为 4,而非默认 4+4,避免过度并发反拖慢响应
- 不使用
mysqld_safe,它会绕过numactl环境,改用裸mysqld进程启动
my.cnf 中必须调整的多核敏感参数(按实例独立配置)
每个实例的 my.cnf 必须差异化设置,否则多实例等于“叠床架屋”:
-
innodb_buffer_pool_instances:设为innodb_buffer_pool_size / 4G向上取整(如 8G 缓冲池 → 值为 2),避免单实例内多线程争抢 buffer pool mutex -
innodb_purge_threads:高写入场景建议设为 4,但所有实例的 purge 线程总数不应超过物理核心数 × 0.5 -
table_open_cache和max_connections:按实例预期负载配比,例如 3307 实例承载 OLTP,设max_connections=200;3308 实例跑报表,设max_connections=30,防止大查询长期占满连接 - 务必关闭
skip_name_resolve,否则 DNS 解析线程会在所有实例间串扰,造成不可预测的 CPU 尖刺
验证是否真的跑在目标核上且无跨 NUMA 访问
启动后立刻检查:
- 查进程绑核:
taskset -cp $(pgrep -f "3307.*my.cnf"),输出应类似pid 12345's current affinity list: 0-3 - 查 NUMA 分布:
numastat -p $(pgrep -f "3307.*my.cnf"),重点关注Node 0下的MemUsed是否远高于Node 1,且Foreign行数值接近 0 - 查线程数:
ps -T -p $(pgrep -f "3307.*my.cnf") | wc -l,结果应 ≈innodb_read_io_threads + innodb_write_io_threads + 1(主线程)+ max_connections × 1.2(估算连接线程)
最容易被忽略的是:没关 innodb_stats_on_metadata —— 它会让每次 SHOW TABLES 触发全表采样,在多实例下引发大量后台 I/O 和 CPU 抢占,生产环境必须设为 OFF。



















