線上 MySQL 實例物理內存 47GB,配置 innodb_buffer_pool_size = 36GB,導致系統可用內存歸零、Swap 佔用 4GB、MySQL 進程被換出 2.1GB,嚴重影響性能。本文完整記錄現象觀測、根因定位、三層解決方案(OS 級、引擎級、內存分配級),並給出生產環境配置建議。
| 項目 | 值 |
|---|---|
| CPU | Intel Xeon E5640 @ 2.67GHz × 16 |
| 物理內存 | 47 GB |
| innodb_buffer_pool_size | 36 GB |
| 數據量 | 115 GB(小型數據倉庫) |
| 業務特徵 | 頻繁大批量導入、讀取 |
total used free shared buffers cached
Mem: 47 46 0 0 0 6
-/+ buffers/cache: 39 7
Swap: 7 4 3
系統可用內存僅 7GB,Swap 已用 4GB。
procs memory swap io system cpu
r b swpd free inact active si so bi bo in cs us sy id wa st
0 0 4741 158 12973 34467 0 0 34 78 1 0 1 0 99 0 0
swpd=4741MB 確認大量換出。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ SWAP CODE DATA COMMAND
12713 mysql 15 0 40.8g 38g 12m S 0.7 82.1 1209:31 2.1g 7660 40g mysqld
# 臨時生效
echo 0 > /proc/sys/vm/swappiness
# 永久生效
echo "vm.swappiness = 0" >> /etc/sysctl.conf
sysctl -p
swappiness=0:盡可能不使用 Swap(內核 3.5+ 不會完全禁用 OOM killer 觸發前的換出)
# my.cnf
[mysqld]
innodb_flush_method = O_DIRECT
# 1. 系統配置大頁
echo "vm.nr_hugepages = 20000" >> /etc/sysctl.conf # 20000 * 2MB = 40GB
sysctl -p
# 2. MySQL 啟用
[mysqld]
large-pages = 1
innodb_buffer_pool_size = 36G
# /etc/security/limits.conf
mysql soft memlock unlimited
mysql hard memlock unlimited
# my.cnf
[mysqld]
memlock = 1
# /etc/sysctl.conf
vm.swappiness = 1
vm.nr_hugepages = 0 # 若不使用 HugePages 設 0
# my.cnf
[mysqld]
innodb_buffer_pool_size = 32G # 留 15GB 給 OS、連接、線程、查詢快取、其他進程
innodb_flush_method = O_DIRECT
innodb_buffer_pool_instances = 8 # 多實例減少鎖競爭
innodb_page_cleaners = 4
innodb_lru_scan_depth = 2000
# 不啟用 large-pages / memlock,除非極度確定
Innodb_buffer_pool_pages_free 長期 < 10% 考慮擴容
# 部署後觀察 24-48h
free -h # Swap used 應穩定在極低值(< 100MB)
vmstat 1 # si/so 持續為 0
SHOW ENGINE INNODB STATUS \G # Buffer pool 狀態正常
內存規劃是 MySQL 性能的基石。buffer_pool 不是越大越好,必須為 OS 預留足夠內存。O_DIRECT 是低成本高收益的必開選項,swappiness=1 防誤換出。HugePages/memlock 屬於深度優化,風險較大,非核心瓶頸不建議輕易嘗試。