幫助中心

伺服器、網路和帳戶的分步教程。

Linux 伺服器

1GB / 2GB 小記憶體 VPS 的 MySQL 與 MariaDB 最佳化:真正重要的 my.cnf 引數

1GB / 2GB 小記憶體 VPS 的 MySQL 與 MariaDB 最佳化:真正重要的 my.cnf 引數

在 1 GB 或 2 GB 記憶體的 VPS 上,只要讓幾項關鍵的 my.cnf 引數與實際記憶體相匹配,MySQL 和 MariaDB 就能跑得又快又穩:innodb_buffer_pool_size、max_connections、臨時表上限以及 performance_schema。本文提供兩種記憶體規格可直接使用的配置,說明如何在 Debian、Ubuntu、Rocky Linux 與 AlmaLinux 上安全應用並檢查效果,以及如何找出任何引數都救不了的慢查詢。

示例適用於 JUSTG 雲伺服器套餐 01(1 GB 記憶體、20 GB SSD)與 02(2 GB 記憶體、40 GB SSD),並假設資料庫與 Web 伺服器、PHP 部署在同一臺 VPS 上。以下命令請以 root 執行;如果 mysql 要求輸入密碼,請加上 -u root -p。在 MariaDB 11(Debian 13)上,如果沒有 mysql… 命令,請改用對應的 mariadb… 名稱(mariadb、mariadb-dump、mariadb-dumpslow)。

步驟 1:檢視版本與當前記憶體佔用

部分引數只有 MySQL 或只有 MariaDB 才支援,所以先確認你使用的是哪種資料庫、哪個版本,以及它現在佔用了多少記憶體。

mysql --version                 # MariaDB 11 (Debian 13): mariadb --version
mysql -e "SELECT VERSION();"
free -h
ps -o pid,rss,cmd -C mysqld,mariadbd    # RSS = memory really used, in KB

RSS 的單位是 KB(350000 約等於 340 MB)。如果正常負載下 free -h 中的 available 已低於約 150 MB,應先調低引數,而不是調高。常見版本:Debian 12 自帶 MariaDB 10.11,Debian 13 為 MariaDB 11.8,Ubuntu 22.04/24.04 可安裝 MySQL 8.0 或 MariaDB。

步驟 2:先備份資料庫與配置

修改配置本身不會動到資料,但測試時的失誤可能會。請先做一次完整匯出,並複製配置目錄。如果硬碟快滿了,請參考Linux 硬碟空間已滿怎麼辦?VPS 找出並釋放磁碟空間教程釋放空間。

df -h /                         # enough free space for the dump?
mkdir -p /root/db-backup
mysqldump --all-databases --single-transaction --routines --events \
  | gzip > /root/db-backup/all-$(date +%F).sql.gz
# MariaDB 11 (Debian 13): use mariadb-dump with the same options

cp -a /etc/mysql /root/db-backup/etc-mysql-$(date +%F)
# Rocky / AlmaLinux: cp -a /etc/my.cnf /etc/my.cnf.d /root/db-backup/
ls -lh /root/db-backup

--single-transaction 可以在不鎖住網站的情況下得到 InnoDB 表的一致性備份(對 MyISAM 表無效,這類表請在低峰時段匯出)。

步驟 3:找到配置檔案的位置

不要直接修改體積龐大的預設配置檔案。請在 include 目錄中新建一個自己的小檔案:它在預設檔案之後讀取,其中的值會覆蓋預設值,而且軟體包升級時不會被覆蓋。

系統include 目錄預設服務端配置檔案
Debian 12/13、Ubuntu 22.04/24.04 + MariaDB/etc/mysql/mariadb.conf.d/50-server.cnf
Ubuntu 22.04/24.04 + MySQL 8.0/etc/mysql/mysql.conf.d/mysqld.cnf
Rocky Linux / AlmaLinux(MariaDB 或 MySQL)/etc/my.cnf.d/mariadb-server.cnf 或 mysql-server.cnf

無論哪種情況,都在該目錄中新建 zz-tuning.cnf。配置檔案按字母順序讀取,zz- 字首能確保你的檔案最後載入;檔名必須以 .cnf 結尾。

步驟 4:1 GB 與 2 GB 的推薦配置

請從與你記憶體相符的配置開始。兩套配置都為同一臺 VPS 上的 Web 伺服器和 PHP 留出了空間。

1 GB VPS

# zz-tuning.cnf - 1 GB VPS, database shares the server with nginx/Apache and PHP
[mysqld]
performance_schema      = OFF
skip-name-resolve

innodb_buffer_pool_size = 256M
max_connections         = 40
thread_cache_size       = 8

tmp_table_size          = 32M
max_heap_table_size     = 32M

table_open_cache        = 400
table_definition_cache  = 400
key_buffer_size         = 16M

# 1 = safest (default), see Step 5
innodb_flush_log_at_trx_commit = 1

2 GB VPS

# zz-tuning.cnf - 2 GB VPS, database shares the server with nginx/Apache and PHP
[mysqld]
performance_schema      = OFF
skip-name-resolve

innodb_buffer_pool_size = 512M
max_connections         = 80
thread_cache_size       = 16

tmp_table_size          = 64M
max_heap_table_size     = 64M

table_open_cache        = 1000
table_definition_cache  = 800
key_buffer_size         = 32M

# 1 = safest (default), see Step 5
innodb_flush_log_at_trx_commit = 1
  • innodb_buffer_pool_size:InnoDB 快取資料和索引的記憶體,是最重要的數值。常說的“設為記憶體的 70%–80%”只適用於專門跑資料庫的伺服器。同一臺 VPS 上還有 Web 環境時,從記憶體的 25% 左右開始;只跑資料庫的 VPS,1 GB 可設約 512M,2 GB 可設約 1G。
  • max_connections:每個連線都可能分配自己的緩衝區(見步驟 10),所以預設的 151 在小套餐上風險很大。PHP 網站通常只需要 PHP-FPM 程序數,再加幾個給計劃任務和管理工具的連線。
  • tmp_table_size 與 max_heap_table_size:記憶體臨時表的上限(GROUP BY、ORDER BY、DISTINCT)。兩者取較小值生效,請設成相同。調大它們主要只會提高單個查詢在最壞情況下的記憶體佔用。
  • table_open_cache 與 table_definition_cache:只有幾個網站的 VPS 設幾百就夠;如果 Opened_tables 持續增長(見步驟 6),再調大。
  • performance_schema = OFF:MySQL 8 預設開啟,可能佔用 100 MB 以上;MariaDB 預設已關閉。關閉後會失去部分診斷檢視,對小套餐來說很划算。
  • skip-name-resolve:新建連線時不再做 DNS 反查,DNS 變慢時登入也不會卡住。啟用後,資料庫帳號必須按 IP 地址或 localhost 定義,不能再用主機名。
  • key_buffer_size:只快取 MyISAM 索引(MariaDB 預設 128 MB);使用 InnoDB 的網站只需要很少。

啟用 skip-name-resolve 前請先列出所有帳號;如果 host 是 app.example.com 這樣的主機名,請改用 IP(例如 203.0.113.10)重新建立該帳號。在 MySQL 8 上,還請檢視二進位制日誌佔用了多少空間:

mysql -e "SELECT user, host FROM mysql.user;"
mysql -e "SHOW BINARY LOGS;"      # MySQL 8: how much space binary logs use

按資料庫版本新增的引數

有些引數只有其中一種資料庫支援。請把與你版本相符的那一段加到同一個檔案的 [mysqld] 下,1 GB 記憶體時按註釋調整數值:

# --- MySQL 8.0.30 or newer only (Ubuntu MySQL 8.0, MySQL 8.4) ---
# Redo log: default 100M is fine on 1 GB; 256M for write-heavy sites on 2 GB
innodb_redo_log_capacity   = 256M
# RAM cap for internal temporary tables: 64M on 1 GB, 128M on 2 GB
temptable_max_ram          = 128M
# Keep binary logs 3 days instead of the default 30
binlog_expire_logs_seconds = 259200

# --- MariaDB, or MySQL older than 8.0.30 ---
# Redo log: default 96M (MariaDB) is fine on 1 GB; 128M-256M on 2 GB
innodb_log_file_size       = 128M

二進位制日誌用於主從複製和按時間點恢復;繁忙網站 30 天的二進位制日誌足以寫滿 20 GB 的硬碟。如果這兩項都用不到,MySQL 8 也支援 skip-log-bin。

千萬不要照抄你的資料庫不認識的引數。MariaDB 遇到 innodb_redo_log_capacity 時會報 unknown variable 'innodb_redo_log_capacity' 並拒絕啟動;MySQL 8 已移除查詢快取,會拒絕 query_cache_size。修改後如果無法啟動,請檢視日誌(步驟 6),改正那一行後再啟動。

步驟 5:innodb_flush_log_at_trx_commit 的真實取捨

這個引數決定了 VPS 斷電或核心崩潰時,你可能丟失多少已提交的資料。請根據網站能承受多少資料損失來選擇,而不是看跑分。

  • 1(預設):每次提交都把 redo 日誌刷入硬碟,已提交的資料不會丟失。適用於商城、計費、使用者帳號等一切涉及金錢的業務。
  • 2:每次提交寫入作業系統快取,約每秒刷盤一次。作業系統或 VPS 崩潰時最多丟約 1 秒的事務;僅資料庫崩潰時不丟。部落格、快取、統計等場景可以接受。
  • 0:約每秒寫入並刷盤一次,即使只是資料庫本身崩潰,也可能丟約 1 秒。很少比 2 更好,不推薦。

設為 2 能明顯加快大量小寫入,但這是用永續性換速度。拿不準時請保持 1;在應用程式中把多次小寫入合併到一個事務裡,是更安全的提速方法。

步驟 6:安全應用並檢查結果

先檢查配置檔案,再重啟服務並立即檢視日誌。請使用你的資料庫對應的服務名。

# 1. Show the options the server will read (MariaDB: mariadbd --print-defaults)
mysqld --print-defaults
# MySQL 8.0.16+ only: check the option files without starting the server
mysqld --validate-config

# 2. Restart and read the log
# Service name: mariadb (MariaDB) | mysql (MySQL on Ubuntu) | mysqld (MySQL on Rocky/Alma)
systemctl restart mariadb
systemctl status mariadb --no-pager
journalctl -u mariadb -n 30 --no-pager

然後確認新值已生效,並檢視狀態計數器:

mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','max_connections','tmp_table_size','performance_schema','skip_name_resolve');"

mysql -e "SHOW GLOBAL STATUS WHERE Variable_name IN ('Uptime','Max_used_connections','Created_tmp_tables','Created_tmp_disk_tables','Opened_tables','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests','Aborted_connects');"

狀態計數器是從上次啟動累計至今的數值,請在正常執行至少一天後再看(Uptime 單位為秒)。

  • Max_used_connections 接近 max_connections:應用開啟了過多連線,或 PHP 程序太多。先解決這個問題,再考慮提高上限。
  • Created_tmp_disk_tables 超過 Created_tmp_tables 的約 25%:在慢查詢日誌中找出這些查詢(步驟 7)。加索引通常比調大 tmp_table_size 更有效。
  • Innodb_buffer_pool_reads 超過 Innodb_buffer_pool_read_requests 的 1%:熱資料放不進緩衝池。只有在 free -h 顯示還有可用記憶體時才調大。
  • Opened_tables 預熱後仍快速增長:以每次約 200 的幅度調大 table_open_cache。

如果 Aborted_connects 不斷上升,通常是有機器人在連 3306 連接埠;若無需對外開放,請按Linux 防火牆設定教程:VPS 使用 UFW 與 firewalld將其封鎖。

步驟 7:用慢查詢日誌找出慢查詢

大多數“資料庫很慢”的問題,根源都是少數幾條糟糕的查詢,而不是配置。慢查詢日誌會記錄每一條執行時間超過 long_query_time 秒的查詢。

# Turn the slow query log on now (until the next restart)
mysql -e "SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 1;"
mysql -e "SHOW VARIABLES LIKE 'slow_query_log_file';"

如需重啟後仍保持開啟,請在 zz-tuning.cnf 中加入:

# Add to zz-tuning.cnf to keep it after restarts
[mysqld]
slow_query_log      = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time     = 1
# Rocky / AlmaLinux with MariaDB: /var/log/mariadb/mysql-slow.log

新的 long_query_time 只對新建立的連線生效。讓日誌收集一天的正常流量後,再進行彙總:

# Top 10 query patterns by total time (MariaDB 11: mariadb-dumpslow)
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

# Turn the log off again when you are done
mysql -e "SET GLOBAL slow_query_log = 0;"

mysqldumpslow 會把相似的查詢歸為一組,把數字替換為 N、字串替換為 'S',並顯示每類查詢的執行次數和耗時。從最上面開始處理:執行 10,000 次、每次 0.2 秒的查詢,代價遠高於只執行一次、耗時 5 秒的查詢。日誌開啟期間會持續增長,用完請關閉,或確認 /etc/logrotate.d/ 會輪轉它。

步驟 8:索引與 EXPLAIN 入門

在慢的 SELECT 前加上 EXPLAIN,就能看到資料庫如何執行它。示例使用一張假設的 orders 表:

EXPLAIN SELECT id, total FROM orders
WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;

SHOW INDEX FROM orders;

ALTER TABLE orders ADD INDEX idx_customer_created (customer_id, created_at);

-- Run EXPLAIN again: type should change from ALL to ref, key = idx_customer_created
EXPLAIN SELECT id, total FROM orders
WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;

輸出中,type = ALL 且 key = NULL 表示全表掃描;ref、eq_ref 或 range 表示用上了索引。只返回 20 行的查詢卻有很大的 rows 估算值,通常說明缺少索引;Extra 中出現 Using filesort 或 Using temporary,表示需要額外排序。

  • 先給 WHERE 中的列建索引(用 = 比較的列放在最前面),再考慮 ORDER BY 的列。
  • 不要對索引列使用函式:WHERE DATE(created_at) = '2026-11-01' 用不上索引,而 created_at >= '2026-11-01' AND created_at < '2026-11-02' 這樣的範圍條件可以。
  • 不要給每一列都建索引:每個索引都會拖慢寫入,並佔用緩衝池記憶體。
給大表新增索引需要時間、CPU 和額外的硬碟空間。MySQL 8 與 MariaDB 大多數情況下可以線上建索引,但伺服器負載仍會升高,請在低峰時段、並在完成步驟 2 的備份後再操作。

步驟 9:把 mysqltuner 當作參考意見

mysqltuner 會讀取資料庫的狀態計數器,並列出可能的最佳化方向。Debian 和 Ubuntu 的軟體源中可直接安裝;Rocky/AlmaLinux 一般可從 EPEL 獲取。

apt install mysqltuner          # Debian / Ubuntu
mysqltuner

使用時請注意:至少正常執行 24 小時後再執行;它不知道同一臺 VPS 上還有 PHP 和 Web 伺服器,可能建議一個讓它們無處容身的緩衝池大小。請用 free -h 核對每一條建議,並且每次只改一項。請把它的輸出當作待確認的問題清單,而不是操作指令。

步驟 10:避免被 OOM killer 殺掉

記憶體耗盡時,Linux 的 OOM killer 會終止佔用記憶體最多的程序,通常就是資料庫。調大任何引數之前,先粗略算一下最壞情況:

# Per-connection buffers (about 2-3 MB per connection with defaults)
mysql -e "SHOW VARIABLES WHERE Variable_name IN ('sort_buffer_size','join_buffer_size','read_buffer_size','read_rnd_buffer_size','thread_stack');"

# Rough worst case, 1 GB profile:
#   buffer pool 256 MB + overhead ~60 MB + 40 connections x ~2.5 MB ~100 MB
#   = ~420 MB, leaving ~500 MB for the OS, web server and PHP

# Has the kernel already killed the database?
journalctl -k | grep -i -E 'out of memory|killed process'

實際佔用通常更低,因為這些緩衝區只在查詢需要時才分配;但每個查詢還可能使用最大為 tmp_table_size 的臨時表,所以請按峰值規劃。不要在全域性把 sort_buffer_size 或 join_buffer_size 調到好幾 MB:乘以 max_connections 之後,很快就會超過 1 GB 套餐的記憶體。如果某個報表確實需要較大的排序緩衝區,只在該連線中執行 SET SESSION sort_buffer_size = 8 * 1024 * 1024; 即可。

請新增 swap 作為應對短時高峰的緩衝,方法見Linux VPS 新增 Swap 交換空間教程。Swap 能防止崩潰,卻不能讓記憶體不足的資料庫變快;如果調優後記憶體依舊緊張,真正的解決辦法是升級到更大的套餐,詳見VPS 升級記憶體、SSD 硬碟或 IP 教程。

常見問題

要不要開啟查詢快取(query cache)?

不要。MySQL 8 已移除查詢快取;MariaDB 雖然保留(預設關閉),但在併發寫入時會因鎖競爭成為瓶頸。請改在應用層做快取,例如使用物件快取。

重啟後修改沒有生效,為什麼?

通常是檔案放在了未被 include 的目錄、檔名不是以 .cnf 結尾,或者之後讀取的檔案又設定了同一個引數。mysqld --print-defaults(MariaDB 為 mariadbd --print-defaults)可以顯示最終生效的引數列表,SHOW VARIABLES 則顯示實際執行中的值。

512 MB 的 mini 套餐能跑資料庫嗎?

小網站可以,但需要 swap 和精簡的配置,例如緩衝池 128M、20 個連線並關閉 performance_schema。在這種記憶體規格下,MariaDB 通常更省資源;訪問量較大的網站,選擇 1 GB 及以上的套餐會更從容。

如果資料庫一再被殺掉,或者不確定該調整哪個引數,請至 https://www.justg.com/submitticket.php 提交工單,並附上伺服器 IP、你的 zz-tuning.cnf 以及 free -h 的輸出。JUSTG 提供 7×24 小時 AI 客服(工單),需要人工處理的問題會由工程師跟進。

這篇文章有幫助嗎?
幫助中心
Linux 伺服器 Linux SSH 金鑰登入教程:設定 ed25519 金鑰並禁用密碼登入 只要伺服器有公網 IPv4,上線幾分鐘內就會遇到 SSH 暴力破解機器人,而密碼登入正是它們的首要目標。本文說明如何用較新的 ed25519 金鑰完成 Linux SSH 金鑰登入,再禁用 SSH 密碼登入,全程避免把自己鎖在門外。步驟適用於 Debian… Linux 伺服器 Linux VPS 修改 SSH 連接埠與 root 密碼教程(安全不斷線) 把 SSH 從 22 連接埠移走雖然擋不住有心的攻擊者,卻能讓日誌裡大部分自動掃描消失;而任何新伺服器的第一件事,就是換一組新的 root 密碼。本文說明如何在 Linux VPS 修改 SSH 連接埠與 root 密碼且不斷線:修改 sshd_config、在 Rocky… Linux 伺服器 Linux 防火牆設定教程:VPS 使用 UFW 與 firewalld(含 IPv6) 新安裝的 Linux 伺服器,任何服務開啟的連接埠都會直接對外響應,因此主機防火牆是最先要設定的專案之一。本文說明如何在 Debian / Ubuntu 用 UFW 設定 Linux 防火牆,以及在 CentOS、Rocky Linux、AlmaLinux 使用… Linux 伺服器 Linux VPS 新增 Swap 交換空間教程(適合 512MB / 1GB 小記憶體套餐) 在 512 MB 或 1 GB 的雲伺服器上,資料庫、PHP-FPM 加上一次軟體包更新,很容易在同一時間用光記憶體。這時核心的 OOM killer 會強制終止程序,最常被犧牲的就是 MySQL 或 MariaDB。在 Linux VPS 新增 Swap… Linux 伺服器 Linux 硬碟空間已滿怎麼辦?VPS 找出並釋放磁碟空間教程 “No space left on device”幾乎會讓伺服器上所有東西出問題:資料庫無法寫入、網站返回錯誤、軟體包更新失敗,有時甚至無法正常登入。本文一步步說明如何排除 Linux 硬碟空間已滿:先確認是資料塊還是 inode 用完,再用 du 與 ncdu…
AlipayUnionPayVISAMastercardPayPalUSDTstripe