
在 ubuntu linux 的 wordpress 主機上架 mysql + apache, 偶爾會遇到 web server crash, 已加入 2GB 的 swap 一樣無解。
結論來說, 在只有 1GB RAM 的主機上安裝 wordpress, 我修改編輯 MySQL 設定檔, 指令:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
內容:
max_connections = 400
我修改編輯 Apache 設定檔, 指令:
sudo nano /etc/apache2/mods-available/mpm_prefork.conf
內容:
MaxRequestWorkers 50
MaxConnectionsPerChild 1000
根據你提供的 /var/log/syslog 記錄,MySQL 會被重啟的原因是 記憶體不足(Out of Memory, OOM),導致 Linux 核心啟動了 OOM Killer,強制結束了 mysqld 進程。這可從以下關鍵日誌判斷:
Jun 3 15:44:43 ubuntu-s-blog-03 kernel: oom-kill:constraint=CONSTRAINT_NONE,...,task=mysqld,pid=8350
Jun 3 15:44:43 ubuntu-s-blog-03 kernel: Out of memory: Killed process 8350 (mysqld)
為什麼會被 OOM Kill?
Linux 當記憶體資源耗盡時,會自動啟用 OOM Killer 終止佔用大量記憶體但可重啟的進程,以避免整個系統崩潰。在這裡 mysqld 可能是記憶體占用最高的進程,因此被選中。
如何避免 MySQL 被 OOM Kill?
✅ 1. 增加可用記憶體
- 升級實體機器或虛擬機的記憶體容量(RAM)。
- 確認是否有記憶體限制(如 cgroup 限制或 docker 容器限制):
systemctl show mysql.service | grep Memory
✅ 2. 設定 swap 空間
如系統記憶體過小,確保你有適當的 swap 空間:
free -h # 查看 swap 使用狀況
若無 swap,可新增:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
✅ 3. 降低 MySQL 記憶體使用
編輯 MySQL 設定檔(如 /etc/mysql/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf),調整以下參數:
[mysqld]
innodb_buffer_pool_size = 256M # 預設可能是幾百 MB,可適度調低
max_connections = 100 # 若過高,會占大量記憶體
query_cache_size = 0 # 建議停用
重新啟動 MySQL:
sudo systemctl restart mysql
✅ 4. 設定 oom_score_adj
讓系統避免優先殺掉 MySQL:
# 建立 systemd override 設定
sudo systemctl edit mysql.service
加入以下內容:
[Service]
OOMScoreAdjust=-500
然後重啟 systemd 設定與 MySQL:
sudo systemctl daemon-reexec
sudo systemctl restart mysql
✅ 5. 找出耗記憶體的其他進程
你可以透過 htop 或 top 查看哪個程序佔用過多記憶體,有時不是 MySQL,而是其他服務造成系統整體 OOM:
ps aux --sort=-%mem | head -n 15
建議總結:
| 預防方法 | 操作建議 |
|---|---|
| 增加記憶體或 swap | 增加 RAM 或新增 swap 空間 |
| 降低 MySQL 記憶體需求 | 調整 innodb_buffer_pool_size, max_connections 等參數 |
| 降低被 OOM Kill 機率 | 設定 OOMScoreAdjust=-500 |
| 觀察整體記憶體使用 | 使用 htop 或 ps 監控其他程序 |
如果你希望我幫忙分析目前 MySQL 使用了多少記憶體,或協助調整具體參數,可提供 free -h 與 ps aux --sort=-%mem | head -n 20 輸出內容,我可以進一步建議。
你執行的指令輸出顯示了 MySQL 服務當前的記憶體使用與限制狀況,以下是重點分析:
🔍 目前記憶體狀態解析
| 欄位名稱 | 解釋 |
|---|---|
MemoryCurrent=181497856 | MySQL 當前佔用約 181 MB 的記憶體。這並不算高,代表 OOM 當下 MySQL 的實際 RSS 是比較低的(可能已經釋放一部分)。 |
MemoryAccounting=yes | 系統啟用了記憶體統計。這是好事,能追蹤單個服務記憶體使用情況。 |
MemoryMax=infinity | 沒有限制 MySQL 的記憶體使用上限。 |
MemorySwapMax=infinity | 同樣未限制 swap 的使用。 |
MemoryLimit=infinity | 沒有設定總記憶體限制。 |
✅ 結論:目前沒有針對 MySQL 的記憶體限制,OOM Kill 並非 systemd 層面的限制所致。
這表示造成 OOM Kill 的根本原因可能是:
🚨 整台主機的總記憶體不足(包括其他進程的佔用)。
✅ 接下來建議你進行以下動作:
1. 🔍 檢查整體記憶體與 swap 使用狀況
free -h
2. 🔍 查找當時記憶體佔用前幾名的進程
ps aux --sort=-%mem | head -n 20
特別關注是不是有:
- PHP-FPM(常見於 WordPress 主機)
- Java / Node / Python 程式吃掉大量記憶體
- 長時間運行的備份或同步程序
📌 如果你希望立即減少 OOM 風險:
✅ 建議 1:限制 MySQL 的最大記憶體使用(例如 512 MB)
可用 systemd cgroup 設定:
sudo systemctl edit mysql.service
加入以下內容:
[Service]
MemoryMax=512M
然後重新載入與重啟:
sudo systemctl daemon-reexec
sudo systemctl restart mysql
這樣即使系統吃緊,MySQL 也不會吃掉過多記憶體,但同時要搭配調整 MySQL 自己的設定(如 innodb_buffer_pool_size)避免啟動失敗。
✅ 建議 2:設置 OOM score 避免 MySQL 被優先殺死
sudo systemctl edit mysql.service
加入:
[Service]
OOMScoreAdjust=-500
這樣 OOM 發生時系統會選擇其他進程(如 cache-heavy script)先殺。
✅ 建議 3:新增或擴增 swap 空間
如果實體記憶體不夠,設置至少 1~2GB 的 swap 空間 可以避免突然 OOM:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
如果你想從 MySQL 自身設定(mysqld.cnf) 下手,來降低記憶體使用、防止 OOM,可以調整以下幾個核心參數。這些主要影響的是 MySQL 在 InnoDB 引擎中的快取、查詢緩衝、連線數等資源分配。
✅ 建議修改的 mysqld.cnf 參數(防止佔用過多記憶體)
路徑範例:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
加入或修改以下內容(在 [mysqld] 區段下):
[mysqld]
# 降低 buffer pool 記憶體用量
innodb_buffer_pool_size = 128M # 預設可能是 256M~1G,可根據 RAM 降低
# 降低連線對記憶體的需求
max_connections = 50 # 預設常常是 151~200,這會分配大量 thread 和 buffer
# 避免不必要的查詢快取
query_cache_type = 0
query_cache_size = 0
# 限制內部快取區大小
sort_buffer_size = 1M
join_buffer_size = 1M
read_buffer_size = 1M
read_rnd_buffer_size = 1M
# 限制 thread 快取與堆疊大小
thread_stack = 192K
thread_cache_size = 4
# 限制 temporary table 使用的記憶體
tmp_table_size = 16M
max_heap_table_size = 16M
🧠 為什麼這些參數重要?
| 參數 | 作用說明 |
|---|---|
innodb_buffer_pool_size | InnoDB 的主要記憶體快取,太大會佔用大量記憶體。 |
max_connections | 每個連線都會產生一堆 buffer,連線太多會吃爆記憶體。 |
query_cache_* | 查詢快取常被禁用,因為會影響效能,且在新版 MySQL 預設已關。 |
*_buffer_size | 每個連線都會有這些 buffer,值太大乘上連線數會爆炸。 |
thread_cache_size | 控制 Thread 資源重用,不宜太高。 |
tmp_table_size | 超過此大小的臨時表會寫入磁碟,設定太大會吃記憶體。 |
✅ 套用設定並重啟 MySQL
sudo systemctl restart mysql
✅ 驗證是否套用成功
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
或:
mysqladmin variables -u root -p | grep buffer
✅ 建議依系統記憶體調整的配置範例:
| 系統總記憶體 | 建議 innodb_buffer_pool_size | max_connections |
|---|---|---|
| 512MB | 64M~128M | 30~50 |
| 1GB | 128M~256M | 50~80 |
| 2GB | 256M~512M | 100~150 |
你目前的 innodb_buffer_pool_size = 134217728,換算為:
134217728 bytes = 128 MB
這代表你已經手動或預設限制了 InnoDB 的快取為 128MB,這是非常保守的設定,適合記憶體只有 512MB~1GB 的小型機器,因此:
✅ 初步結論:
MySQL 本身記憶體設定已經很節省,不是主因造成 OOM。
✅ 推測你的系統可能還有這些問題:
- 整體記憶體不足(MySQL + PHP-FPM / Apache / nginx / crons / 其他程式吃太多)
- 沒有啟用 swap 或 swap 太小
- MySQL 雖然 pool 小,但同時連線數太多(例如
max_connections很大,每條連線佔用額外 buffer) - 某些查詢導致大量 temporary table 或排序操作吃記憶體
✅ 建議你繼續檢查以下幾個方向:
🔍 1. 查看目前最大允許連線數與平均使用量
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"
mysql -u root -p -e "SHOW STATUS LIKE 'Max_used_connections';"
若 max_connections 是 151,而 Max_used_connections 只有 10~20,就可以調降設定:
max_connections = 50
🔍 2. 查目前各類 buffer 設定(單連線可能吃掉很多)
mysql -u root -p -e "SHOW VARIABLES LIKE '%buffer%';"
特別注意:
sort_buffer_sizejoin_buffer_sizeread_buffer_sizeread_rnd_buffer_size
這些都是 per-thread 分配 的快取,每條連線都會各自分一份。
即使每個只有 1MB,100 條連線就可能額外吃掉 400MB。
你可以設定保守值,例如:
sort_buffer_size = 512K
join_buffer_size = 512K
read_buffer_size = 512K
read_rnd_buffer_size = 512K
🔍 3. 確認是否有 swap,可用以下指令:
swapon --show
free -h
若沒有 swap,可參考我前面提供的指令新增一個 swapfile(可防止系統直接 OOM Kill)。
✅ 小結
你目前 innodb_buffer_pool_size = 128MB 已經很節省,應該進一步優化以下幾點:
- 調低
max_connections,例如設為 50 以下 - 調小每個連線的 buffer size(sort/join/read buffer)
- 確保系統有啟用 swap
- (選擇性)透過 systemd 設定
OOMScoreAdjust=-500,避免 MySQL 被優先殺掉
如果你願意貼上 SHOW VARIABLES LIKE '%buffer%'; 及 SHOW STATUS LIKE 'Max_used_connections'; 的輸出,我可以進一步幫你微調設定。
這裡的資訊非常關鍵,顯示你目前 MySQL 記憶體壓力的「根本問題」:
❗ 你設定了 max_connections = 4000,但實際只用到 140
這代表:
- MySQL 預留了支援 4000 條連線所需的潛在記憶體空間
- 即使實際只有 140 條連線在用,MySQL 還是會根據 max_connections 分配內部資源池(如 Thread Cache、Memory Pool 等)
- 當伺服器記憶體不足時,Linux OOM Killer 極可能選擇記憶體佔用最高的 mysqld 來殺
✅ 解決方案:立刻調整 max_connections!
根據你目前 Max_used_connections = 140,建議保留一些餘裕,設定為:
max_connections = 200
這已經遠高於你曾經的最高同時連線數,足夠支援高峰。
✅ 如何修改
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
在 [mysqld] 區段加上或修改:
max_connections = 200
儲存後,重啟 MySQL:
sudo systemctl restart mysql
🧠 延伸建議(可選)
- 若你使用某些 ORM 框架(如 Laravel、Django),請確認是否有不當的連線池配置。
- 加上 swap(若還沒啟用),讓系統在記憶體不足時能撐住,不直接觸發 OOM。
✅ 調整後的效益
| 項目 | 原始狀態 | 調整後 |
|---|---|---|
| max_connections | 4000 | ✅ 200(實際只用 140) |
| 潛在記憶體佔用 | 非常高 | 顯著降低 |
| OOM 發生機率 | 非常高 | 大幅下降 |
| 系統穩定性 | 容易崩潰 | ✅ 提升 |
如果你有多個服務(如 PHP-FPM)同時吃記憶體,我也可以幫你一併評估哪個才是主因,或是否需要調整 systemd 的保護參數(像 MemoryMax= 或 OOMScoreAdjust=)。如果需要的話可以貼上 free -h 給我參考記憶體總量。
從你提供的 MySQL buffer 設定來看,整體已經很節省,但有幾個關鍵調整可以幫你進一步「防止 OOM(Out of Memory)」:
✅ 重點分析與建議:
🔹 max_connections = 4000(⚠️ 非常危險)
- 這會導致 MySQL 預留大量 memory pool 給潛在連線使用,即使目前只有 140 條連線
- 每條連線會額外吃掉下列 buffer:
| Buffer 名稱 | 大小 | per-connection |
|---|---|---|
join_buffer_size | 256 KB | ✅ 是 |
read_buffer_size | 128 KB | ✅ 是 |
read_rnd_buffer_size | 256 KB | ✅ 是 |
sort_buffer_size | 256 KB | ✅ 是 |
net_buffer_length | 16 KB | ✅ 是 |
假設這 5 個 buffer 加總為約 912KB,一旦 1000 條連線啟動,就會佔用:
912KB * 1000 ≈ 900MB(僅 per-thread buffer,不含 InnoDB)
🔧 強烈建議:
max_connections = 200
🔹 innodb_buffer_pool_size = 128MB(✅ 很節省,建議保留)
- 適合低記憶體伺服器(如 1GB RAM 以下)
- 如果你的伺服器有更多記憶體(例如 2GB 以上),可以適當調高,例如 256MB 或 512MB 來提升效能
🔹 innodb_log_buffer_size = 16MB(✅ 合理)
- 除非你大量進行批量寫入操作,否則這大小很安全
🔹 sort_buffer_size = 256KB、join_buffer_size = 256KB、read_buffer_size = 128KB(✅ 都是精簡設定)
- 建議保留現狀
- 沒有看到過大的設定,很安全
✅ 結論與最佳化方案
| 項目 | 當前狀況 | 建議做法 |
|---|---|---|
max_connections | 4000 | ✅ 改為 200 |
innodb_buffer_pool_size | 128MB | 可保留,視記憶體調升 |
| 各類 buffer(join/sort) | 非常小 | ✅ 保持現狀 |
| Swap | 不明 | 若無,建議新增 swap |
✍️ 調整方法(mysqld.cnf)
編輯 MySQL 設定檔:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
新增或修改以下內容:
[mysqld]
max_connections = 200
儲存並重啟 MySQL:
sudo systemctl restart mysql
(選擇性)啟用或增加 swap(防止記憶體爆掉)
若你的機器沒有 swap,請執行:
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
如你想一起檢查你的記憶體配置、可用 swap、Apache/Nginx/PHP-FPM 等服務佔用情形,也可以貼上 free -h 與 top -o %MEM 的輸出,我可以幫你全面看系統資源瓶頸。
根據你提供的 top -o %MEM、記憶體與 swap 使用狀況,這台伺服器正在記憶體壓力邊緣運作,以下是具體建議:
🔍 目前系統狀態總結:
🧠 實體記憶體狀況(965MB RAM):
已使用: 659.2MB可用: 64.1MB(非常低,快爆掉)buff/cache: 241.8MB
🔁 Swap 狀況:
- 總共:2048MB(已啟用,👍)
- 已使用:22.4MB(代表 Linux 正在用 swap 撐住)
available memory:71.9MB(可做為 emergency memory)
🧨 問題核心:MySQL + Apache 佔用太多記憶體
🟥 MySQL(PID 747)佔用:
- 454MB(46%)
- 對 1GB RAM 來說太高了
- 雖然
innodb_buffer_pool_size只有 128MB,但實際佔用更大(來自 thread buffers)
🟧 Apache2 每個處理程序:
- 每條大約 50~80MB
- 活動中有超過 10 條進程,合計約佔 500~600MB
- 與 MySQL 加總已經吃掉 >90% 記憶體
✅ 解決方案建議:
① 限制 Apache2 同時連線數(大幅降記憶體消耗)
修改 Apache 設定(預設用 mpm_prefork 模式):
sudo nano /etc/apache2/mods-available/mpm_prefork.conf
修改或加入以下內容(控制佔用記憶體上限):
<IfModule mpm_prefork_module>
StartServers 2
MinSpareServers 2
MaxSpareServers 4
MaxRequestWorkers 20
MaxConnectionsPerChild 1000
</IfModule>
然後重啟 Apache:
sudo systemctl restart apache2
② 降低 MySQL 最大連線數(避免記憶體預留過大)
你的目前設定是 max_connections = 4000,非常危險。請修改為:
[mysqld]
max_connections = 100
然後重啟 MySQL:
sudo systemctl restart mysql
③ (選用)調整 Apache 使用的 MPM 模式
改用 event 模式(比 prefork 更省記憶體):
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo systemctl restart apache2
⚠️ 注意:若你在用 PHP module 模式(非 PHP-FPM),會需要改成 FPM 搭配 event。
④ 檢查是否有必要服務可關閉
例如:
snapdmodemmanagermultipathdunattended-upgrades
你可用這些指令查詢與停用:
systemctl disable --now snapd
systemctl disable --now ModemManager
✅ 最佳化後的期望記憶體佔用:
| 項目 | 預估使用量(MB) |
|---|---|
| MySQL | 200 ~ 250MB |
| Apache2 | 150 ~ 300MB |
| 其他系統 | 100MB |
| 總計 | 500 ~ 650MB |
這樣會讓你有 >300MB 的剩餘空間,讓系統不會吃到 swap 或 OOM。
如果你願意,我也可以幫你生成調整後的 mysqld.cnf 與 mpm_prefork.conf 範例。是否要一起產出?