MySQL 調優基礎(二):Linux 記憶體管理、Swap 避坑與 InnoDB 緩衝池協同優化
繼 CPU 篇後,本文深入 Linux 虛擬記憶體、頁面回收、Swap 機制、OOM Killer、HugePages、vm.swappiness、vm.dirty_ratio 等核心參數,結合 vmstat/sar/pmap/ps 實戰監控,構建 InnoDB 與 OS 記憶體協同優化體系。
一、Linux 虛擬記憶體模型
- 進程見到的是虛擬地址空間(
VIRT),映射至物理頁框(RES)或 Swap;
VIRT = RES + Swap(out) + 未映射保留;
- Linux 激進使用空閒記憶體作 Page Cache (Disk Cache),
free 顯示的 free 很小屬正常;
- Swap 被使用不代表記憶體不足,只說明內核曾將冷頁換出;關鍵看持續 Swap in/out。
二、記憶體分配與回收機制
Buddy System 頁框分配
- 以 4KB Page 為單位,維護 2^n 階空閒鏈表,合併分裂防碎片;
SLAB/SLUB 分配器在頁框上劃分小物件(inode、dentry、task_struct 等)。
Kswapd 守護進程與雙 LRU 列表
Active List:近期訪問頁;Inactive List:候選回收頁;
- 頁面在 Active/Inactive 間升降級,最終從 Inactive 尾部回收;
- 回收優先順序:Page Cache (文件頁) → Anonymous Pages (堆/棧);
vmstat -a 觀察 inact/active 佔比。
三、Swap 與 Page Out 關鍵區別
- Page Out:單頁換出;
- Swap Out:整個進程地址空間換出(極嚴重);
- 監控指標:
vmstat si/so (KB/s)、sar -B pgpgout/s (KB/s)、majflt/s (Major Page Fault/s)。
四、vm.swappiness 與 OOM Killer 避坑指南
| 內核版本 | vm.swappiness=0 行為 | 建議值 |
| 舊內核 (≤2.6.32-302) | 盡量不 Swap,優先回收 Page Cache | 0 |
| 新內核 (≥2.6.32-303 / RHEL6.4+) | 觸發 OOM Killer 直接殺進程 | 1 (不為 0,保留微量 Swap 緩衝) |
OOM Score 調整保護 MySQL
# 查看當前 oom_score_adj (範圍 -1000~1000,越低越不易被殺)
cat /proc/$(pidof -s mysqld)/oom_score_adj
# 設置極低值保護 (舊介面 oom_adj 範圍 -17~15,-17=OOM_DISABLE)
echo -1000 > /proc/$(pidof -s mysqld)/oom_score_adj
# 或舊介面
echo -17 > /proc/$(pidof -s mysqld)/oom_adj
根治之道:保障物理記憶體充足 + vm.swappiness=1 + 監控 majflt/s,而非依賴 OOM 參數規避。
五、Page Cache 回寫調優:vm.dirty_ratio 族參數
| 參數 | 預設 | 含義 | 調優建議 |
| vm.dirty_background_ratio | 10% | 髒頁達此比例,pdflush 異步回寫 | 降至 5% 減少髒頁積壓,增加回寫頻率 |
| vm.dirty_ratio | 20% | 髒頁達此比例,同步阻塞回寫 | 降至 10-15% 避免寫入延遲抖動 |
| vm.dirty_expire_centisecs | 3000 (30s) | 髒頁超時強制回寫 | 視延遲容忍度調整 |
| vm.dirty_writeback_centisecs | 500 (5s) | pdflush 喚醒間隔 | 降低可減少單次回寫量 |
MySQL 自身 innodb_flush_log_at_trx_commit/innodb_max_dirty_pages_pct 也控制髒頁,OS 與 DB 雙層協同調優。
六、實戰監控工具鏈
vmstat 2:觀察 si/so、in/cs、wa;
sar -B 2 10:pgpgout/s (Page Out)、majflt/s (Major Fault) 趨勢;
free -m:關注 -/+ buffers/cache 行的 available;
ps -mp -o THREAD,pmem,rss,vsz,tid,pid:MySQL 線程記憶體佔用;
pmap -x :虛擬記憶體映射分佈(Heap/Stack/Shared Libs/Anonymous/Stack),定位記憶體洩漏;
七、HugePages:減少頁表開銷鎖定物理記憶體
- 預設 4KB 頁,大記憶體 (如 64GB Buffer Pool) 頁表條目達百萬級,TLB Miss 多、頁表佔用大;
- 配置 2MB/1GB HugePages,
vm.nr_hugepages = 32768 (64GB/2MB);
- MySQL 啟用:
innodb_buffer_pool_chunk_size、large-pages (需鎖定記憶體權限 ulimit -l unlimited);
- 代價:HugePages 不可 Swap、不可作他用、啟動預留失敗則 MySQL 無法啟動——務必壓測驗證。
八、記憶體調優決策樹
- 持續
so > 0 / pgpgout/s > 0 / majflt/s > 100 → 記憶體不足或 Swappiness 不當;
- 檢查
vm.swappiness、oom_score_adj、dirty_ratio;
- InnoDB 內部:
innodb_buffer_pool_size 佔物理 70-80%、innodb_max_dirty_pages_pct=75、innodb_io_capacity 匹配磁盤;
- 若物理記憶體確不足 → 增加記憶體或架構分庫分表;
- 考慮 HugePages(專用 DB 伺服器、記憶體 ≥ 32GB、壓測驗證無風險)。
- 上一篇文章: MySQL 5.6 Root 密碼遺忘重置:skip-grant-tables 與 explicit_defaults_for_timestamp 避坑實戰
- 下一篇文章: MySQL 資料庫遷移實戰:六大核心場景完整解析與操作指南
Copyright ©
程式碼之家 All Rights Reserved