首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 服務器內存耗盡佔用大量 Swap 根因分析與解決方案:innodb_buffer_pool_size 過大、swappiness、O_DIRECT、大頁內存

     編輯:MySQL 資料庫相關知識

線上 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(小型數據倉庫)
業務特徵 頻繁大批量導入、讀取

free -g 輸出


              total  used  free shared buffers cached
Mem:           47    46     0      0      0      6
-/+ buffers/cache: 39     7
Swap:           7     4     3

系統可用內存僅 7GB,Swap 已用 4GB。

vmstat -a -S M 4 輸出


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 確認大量換出。

top 輸出(MySQL 進程)


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
  • VIRT 40.8GB(DATA 40GB)
  • SWAP 2.1GB(被換出)
  • RES 38GB(駐留內存)

根因分析

  1. buffer_pool 過大:36GB + 系統開銷 + 其他進程 > 物理內存 47GB,必然觸發 Swap
  2. InnoDB 頁面刷新機制:脏頁刷盤時經過 OS 頁面快取,造成「雙重快取」(InnoDB buffer pool + OS page cache),浪費內存
  3. swappiness 默認 60:OS 傾向於將非活躍頁面換出,MySQL 內存頁面被誤判為非活躍

解決方案三層次

第一層:OS 級 - 調整 swappiness


# 臨時生效
echo 0 > /proc/sys/vm/swappiness
# 永久生效
echo "vm.swappiness = 0" >> /etc/sysctl.conf
sysctl -p
  • swappiness=0:盡可能不使用 Swap(內核 3.5+ 不會完全禁用 OOM killer 觸發前的換出)
  • 注意:設 0 可能增加 OOM 風險,建議設 1-10 保留緩衝

第二層:引擎級 - 繞過 OS 頁面快取


# my.cnf
[mysqld]
innodb_flush_method = O_DIRECT
  • InnoDB 直接對磁塊設備 I/O,不經 OS page cache
  • 消除「雙重快取」,節省內存、減少 Swap 壓力
  • 僅對 InnoDB 生效

第三層:內存分配級 - 大頁內存


# 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
  • 大頁(HugePages)鎖定在物理內存,**永不換出**
  • 減少 TLB miss,提升內存訪問效率
  • 風險:配置複雜、需精確規劃大小、啟動失敗則無法啟動、內存碎片化導致分配失敗

備選方案:memlock(謹慎使用)


# /etc/security/limits.conf
mysql soft memlock unlimited
mysql hard memlock unlimited

# my.cnf
[mysqld]
memlock = 1
  • 鎖定進程內存不被換出
  • 問題多:內存不足直接 OOM、難以動態調整、與 HugePages 衝突

綜合建議配置(生產環境)


# /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,除非極度確定

buffer_pool 尺寸規劃原則

  • 專用 DB 服務器:物理內存的 60-75%(留 25-40% 給 OS)
  • 混部服務器:更保守,50% 以下
  • 監控指標: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 屬於深度優化,風險較大,非核心瓶頸不建議輕易嘗試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved