MySQL 性能不僅取決於 SQL 優化與索引設計,底層 Linux 運行環境的調優同樣關鍵。本文從 IO 調度算法、文件描述符限制、NUMA 架構適配、Swappiness 策略、文件系統掛載參數五大維度,提供生產環境驗證的優化方案,並說明各參數的原理、副作用與驗證方法。
Linux 默認使用 cfq(完全公平隊列)或 mq-deadline,面向通用負載。數據庫屬隨機 IO 密集型,deadline(截止時間調度)能保證讀請求低延遲;SSD/NVMe 無機械尋道,noop 或 kyber(多隊列)更優。
# 1. 臨時生效(重啟失效)
echo deadline > /sys/block/sda/queue/scheduler
# NVMe 設備
echo none > /sys/block/nvme0n1/queue/scheduler # noop 在新內核改名為 none
# 2. 永久生效:修改內核啟動參數
vim /etc/default/grub
# 找到 GRUB_CMDLINE_LINUX,追加:
# HDD/SATA SSD: elevator=deadline
# NVMe: elevator=none
GRUB_CMDLINE_LINUX="... elevator=deadline"
# 更新 grub 並重啟
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
# 3. 驗證
cat /sys/block/sda/queue/scheduler
# 輸出應包含 [deadline]
高併發 MySQL 易觸發 Too many open files、Can't create thread。默認 1024 遠遠不夠。
# 針對 mysql 用戶(或運行 mysqld 的用戶)
mysql soft nofile 1048576
mysql hard nofile 1048576
mysql soft nproc 1048576
mysql hard nproc 1048576
# 通配符配置(所有用戶,慎用)
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 1048576
* hard nproc 1048576
fs.file-max = 2097152
fs.nr_open = 1048576
# 創建覆蓋文件
mkdir -p /etc/systemd/system/mysqld.service.d/
vim /etc/systemd/system/mysqld.service.d/limits.conf
[Service]
LimitNOFILE=1048576
LimitNPROC=1048576
LimitMEMLOCK=infinity
# 重載並重啟
systemctl daemon-reload
systemctl restart mysqld
# 驗證進程實際限制
cat /proc/$(pidof mysqld)/limits | grep -E 'Max open files|Max processes'
多插槽服務器啟用 NUMA 時,MySQL 進程可能被綁定到單節點,導致該節點內存耗盡觸發 Swap,而另一節點閒置。表現為「內存明明還有剩餘,卻發生 Swap/OOM」。
vim /etc/default/grub
GRUB_CMDLINE_LINUX="... numa=off"
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
MySQL 官方 mysqld_safe 腳本內建支持:
# 檢查 mysqld_safe 是否包含 numa-interleave 邏輯(現代版本均有)
grep -n numa-interleave /usr/bin/mysqld_safe
# 啟用:編輯 /etc/my.cnf [mysqld_safe] 區段
[mysqld_safe]
numa-interleave=1
原理:numactl --interleave=all 讓內存跨節點均勻分配,避免單節點熱點。
# 查看節點內存分佈
numastat -m
# 查看 mysqld 進程內存分佈
pid=$(pidof mysqld)
cat /proc/$pid/numa_maps | awk '{sum[$2]+=$4} END {for (n in sum) print n, sum[n]*4/1024 "MB"}'
vm.swappiness = 0:內核 3.5-5.8 盡量不 Swap,但可能觸發 OOM Killer 殺掉 MySQLvm.swappiness = 1:內核 5.8+ 推薦值,允許極少量 Swap,避免 OOMvm.swappiness = 10:保守折中,生產常用vm.swappiness = 60:默認值,桌面導向,服務器不適用
# /etc/sysctl.conf
vm.swappiness = 1
# 立即生效
sysctl -p
# 驗證
sysctl vm.swappiness
cat /proc/sys/vm/swappiness
# 觀察 Swap 讀寫(持續非零表示內存壓力大)
vmstat 1 10
# 關注 si(換入) so(換出) 列
# 查看 MySQL 實際 Swap 佔用
grep VmSwap /proc/$(pidof mysqld)/status
| FS | 優勢 | 劣勢 | 推薦場景 |
|---|---|---|---|
| XFS | 大文件高性能、並行 IO、在線擴容、元數據日誌 | 不支援縮容、小文件元數據開銷大 | MySQL 數據目錄、大表、高併發寫入 |
| ext4 | 成熟穩定、支援縮容、向後兼容 | 大目錄性能弱於 XFS、inode 固定 | 通用、中小數據庫、需縮容場景 |
# XFS 推薦
/dev/mapper/data-mysql /var/lib/mysql xfs defaults,noatime,nodiratime,nobarrier 0 2
# ext4 推薦
/dev/mapper/data-mysql /var/lib/mysql ext4 defaults,noatime,nodiratime,barrier=0 0 2
noatime:不更新訪問時間,消除大量元數據寫入nodiratime:不更新目錄訪問時間nobarrier/barrier=0:關閉寫入屏障,**前提是存儲設備有 BBU(電池備份單元)或 RAID 卡帶備用電**,否則掉電數據風險極大discard(SSD/NVMe 可選):啟用 TRIM,但建議改用定時 fstrim 而非實時
findmnt -T /var/lib/mysql -o TARGET,SOURCE,FSTYPE,OPTIONS
# 或
cat /proc/mounts | grep mysql
#!/bin/bash
# /usr/local/bin/mysql_env_check.sh
echo "=== IO Scheduler ==="
for d in /sys/block/sd* /sys/block/nvme*; do
[ -e "$d/queue/scheduler" ] && echo "$(basename $d): $(cat $d/queue/scheduler)"
done
echo -e "\n=== Limits (mysqld process) ==="
pid=$(pidof mysqld)
cat /proc/$pid/limits | grep -E 'Max open files|Max processes|Max locked memory'
echo -e "\n=== NUMA ==="
numastat -m | head -20
echo -e "\n=== Swappiness ==="
sysctl vm.swappiness
echo -e "\n=== Mount Options ==="
findmnt -T /var/lib/mysql -o TARGET,SOURCE,FSTYPE,OPTIONS
echo -e "\n=== Swap Usage ==="
grep VmSwap /proc/$pid/status
五大維度調優非孤立,需整體規劃:IO 調度配合文件系統、NUMA 配合內存分配、Swappiness 配合物理內存容量。建議在預發環境壓測驗證每項調整的收益,再推廣至生產。調優無銀彈,持續觀測、迭代優化才是正道。