前兩篇揭示:Linux OverCommit 讓 MySQL 成為 OOM Killer 首選目標,禁用 OverCommit 反而增加 Crash 風險。本文給出生產環境驗證的「免殺三板斧」,構建從預防、保護到兜底的立體防禦體系。
Total MySQL Memory ≈
Global Buffers
+ (Thread Buffers × Max Connections)
+ Other (Table Cache, Dict, PS, etc.)
| 參數 | 建議值 | 佔比/說明 |
|---|---|---|
innodb_buffer_pool_size | 10-11 GB | 60-70% 物理內存,核心緩存 |
innodb_buffer_pool_instances | 8-16 | 減少鎖競爭 |
innodb_log_buffer_size | 64-128 MB | 大事務減少刷盤 |
max_connections | 2000-3000 | 配合連接池,避免爆發 |
thread_stack | 256 KB | 默認足夠 |
sort_buffer_size | 2-4 MB | 會話級,慎調大 |
join_buffer_size | 2-4 MB | 會話級,慎調大 |
read_buffer_size | 2-4 MB | 會話級 |
read_rnd_buffer_size | 2-4 MB | 會話級 |
binlog_cache_size | 32 MB | 事務級 |
table_open_cache | 4000-8000 | 減少打開表開銷 |
table_definition_cache | 2000-4000 | |
query_cache_size | 0 | 5.7+ 建議關閉 |
Global ≈ 11.5 GB
Per Thread ≈ 16 MB (sort+join+read+read_rnd+binlog+stack)
Max Threads ≈ 3000
Peak ≈ 11.5 + 16×3000/1024 ≈ 59.5 GB ← 理論值,實際遠低(並非所有連接同時滿載)
# 實際建議:物理內存預留 20-30% 給 OS、FS Cache、監控 Agent、備份進程
# 16GB 實例:MySQL 佔用 ≤ 12 GB,留 4 GB 給系統
-1000 = 完全免疫,系統內存耗盡時無進程可殺 → 內核崩潰/重啟-800 = 分數右移 800 位,實際 oom_score 極低但非零,極端情況仍可被犧牲,保住系統# 方法 1:systemd 服務單元(推薦,持久化)
# /etc/systemd/system/mysqld.service.d/override.conf
[Service]
OOMScoreAdjust=-800
systemctl daemon-reload && systemctl restart mysqld
# 方法 2:啟動腳本注入
echo -800 > /proc/$(pgrep mysqld)/oom_score_adj
# 方法 3:mysqld_safe 封裝
# 在 mysqld_safe 中添加調用
cat /proc/$(pgrep mysqld)/oom_score_adj # -800
cat /proc/$(pgrep mysqld)/oom_score # 極低值(如 0-10)
| 層級 | 指標 | 告警閾值 | 動作 |
|---|---|---|---|
| OS | MemAvailable / MemTotal | < 15% | 觸發熔斷 |
| OS | SwapUsed / SwapTotal | > 10% | 緊急排查 |
| MySQL | Innodb_buffer_pool_pages_total × page_size / RAM | > 75% | 評估擴容 |
| MySQL | Threads_connected / max_connections | > 80% | 限流/擴容 |
| MySQL | Performance Schema memory summary | 內存組件異常增長 | 定位泄漏 |
#!/bin/bash
# 每分鐘跑一次,檢測 MemAvailable < 15% 則 Kill 閒置連接
AVAIL_PCT=$(awk '/MemAvailable/{a=$2} /MemTotal/{t=$2} END{print int(a*100/t)}' /proc/meminfo)
if [ $AVAIL_PCT -lt 15 ]; then
# Kill 睡眠超過 60s 的非超級用戶連接
mysql -e "SELECT CONCAT('KILL ', id, ';') FROM information_schema.PROCESSLIST
WHERE COMMAND='Sleep' AND TIME>60 AND USER NOT IN ('root','repl','monitor')
ORDER BY TIME DESC LIMIT 50" | mysql
logger "MySQL OOM Fuse triggered: killed idle connections, MemAvailable=$AVAIL_PCT%"
fi
mysql-query_rules:匹配高耗時/大結果集查詢 → ERROR_MSG 直接返回mysql_scheduler:定期執行 KILL QUERY 清理長運行事務oom_score_adj=-800、部署熔斷腳本MySQL OOM 免殺無銀彈,只有源頭控制內存佔用、核心調低 oom_score、兜底監控熔斷的組合拳。將三板斧納入標準化部署模板、巡檢清單、變更驗收標準,讓 OOM 成為可控風險而非不可預測的黑天鵝。