帮助中心

服务器、网络和账户的分步教程。

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