
在 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_size
join_buffer_size
read_buffer_size
read_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。
④ 檢查是否有必要服務可關閉
例如:
snapd
modemmanager
multipathd
unattended-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
範例。是否要一起產出?