单机游戏下载单机游戏下载基地
最新游戏|热门游戏|游戏大全|游戏专题

MySQL在小内存VPS上的调参清单

2026/11/11 8:41:50 来源:本站整理 编辑:飞翔小编

您的位置:首页 → 资讯教程 → 软件教程 → MySQL在小内存VPS上的调参清单

【飞翔导读】小内存 VPS 上跑 MySQL,九成问题不是版本而是参数没跟着内存调。这份清单按你实际会搜的问句逐条拆解:buffer pool 该设多少、连接数怎么定、临时表为什么爆内存、日志怎么限制、swap 到底该不该用、监控盯哪几个指标,每条都给具体数值、命令和常见报错原文,末尾附可照抄的参数模板与检查顺序,价格只引用公开商品参数库的真实报价。

一、MySQL 吃掉多少内存才算合理

小内存 VPS 上最常见的问题不是「MySQL 太吃内存」,而是「你不知道它吃了多少」。 先搞清楚分配给数据库的预算,再谈调参。

MySQL 的内存主要由六块组成,知道了这六块,你就知道每个参数该往哪调:

第一块是 InnoDB buffer pool,这是绝对主力。 它缓存数据页和索引页,默认值通常是物理内存的 50% 到 70%。在小内存机器上这个默认值几乎总是错的——它把一半以上的内存锁死给了数据库,而 PHP-FPM 一个 worker 都没得用。

第二块是连接内存,每个连接都要占。 一个活跃连接的基础开销大约 1MB 到 2MB,加上连接执行的临时缓冲。连接数乘以单连接开销,就是这一块的总额。 这是小内存机器上第二容易失控的地方。

第三块是临时表和排序缓冲。 tmp_table_size 和 max_heap_table_size 默认都是 16MB,而这两个值是「单个连接」的。100 个连接同时排序,理论上就是 1.6GB。

第四块是排序缓冲 sort_buffer_size。 默认 256KB,单连接级别,同样是连接数乘。

第五块是连接缓冲 join_buffer_size 和读缓冲 read_buffer_size。 后者是全局级别(所有连接共享),前者是连接级别。

第六块是 redo log buffer 和 binlog cache。 这两块通常不大,但 binlog 开着的话会持续增长。

所以「MySQL 要多少内存」的答案是:

内存 = buffer pool + 连接数 × 每连接开销 + 临时表和排序缓冲 + 日志缓冲 + 约 200MB 的运行开销。

用这个公式算几个具体例子。 一台 2GB 的 VPS,留给 MySQL 512MB 是合理的:buffer pool 设 256MB,连接数限制在 30,每连接按 2MB 算占 60MB,临时表和排序缓冲留 100MB,日志和杂项 96MB,加起来 512MB 刚好。

一台 1GB 的 VPS,留给 MySQL 256MB 就要开始省了: buffer pool 设 128MB,连接数限制 20,临时表设 16MB,排序缓冲 128KB。

这里必须强调一个反直觉的结论:1GB 机器上,MySQL 的 buffer pool 设成 512MB 是灾难性的。 因为 512MB 的 buffer pool 加上 PHP-FPM、加上操作系统,一台 1GB 的机器立刻就在 OOM 边缘。很多人在 1GB 机器上照抄 4GB 机器的配置模板,这就是崩溃的起因。

查当前实际占用用这两条命令:

# MySQL 自己报告的分配情况
mysql -u root -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_bytes_data';"
# 系统视角的进程内存
ps -o rss= -C mysqld | awk '{printf "mysqld RSS: %.0f MB\n", $1/1024}'

两个数字要对得上。 Innodb_buffer_pool_bytes_data 是已使用的数据页大小,ps 看到的 RSS 是进程全部内存(含临时表、连接缓冲、线程栈)。RSS 明显大于 buffer pool,说明连接数和临时表吃掉了很多。

查看 iwebfusion 当前月价与库存

判断标准:mysqld 的 RSS 应该占整台机器内存的 30% 到 40%。 超过 50% 说明数据库在抢 PHP 的内存;低于 15% 说明 buffer pool 设小了,数据库在频繁读盘。

二、innodb_buffer_pool_size 该设多少

这是 MySQL 最重要的一个参数,也是小内存机器上最该先调的一个。

官方推荐是物理内存的 50% 到 70%,这个比例来自「数据库独占一台机器」的假设。 小内存 VPS 上数据库和 PHP 共享内存,这个假设不成立。

先算可用的内存上限。 总内存减去操作系统预留(通常 300MB 到 400MB)、减去 PHP-FPM 的占用、再减一点安全余量。然后这个可用值再打个七折给 buffer pool。

具体到几个常见档位:

机器内存 预留给系统和 PHP 建议 buffer pool 说明
1GB 约 700MB 128M 到 192M 再多 PHP 就跑不动了
2GB 约 1.4GB 256M 到 384M 有余力可以给 512M
4GB 约 2.8GB 768M 到 1G 数据库为主的站点可以给 1.5G
8GB 约 5.6GB 2G 到 3G 留足给 PHP 和 OPcache

查 racknerd 在售套餐与库存

注意 4GB 那行的 1.5G 是有前提的:只有当这台机器主要跑数据库、PHP 是次要负载时才适用。 纯 WordPress 站点给 1.5G buffer pool 是浪费,因为数据量根本用不满,多出来的内存是纯浪费的——buffer pool 是按需分配的,不会因为上限大就真占满,但会阻止其他进程用。

改配置的两种方式。 临时生效(重启后失效):

SET GLOBAL innodb_buffer_pool_size = 512 * 1024 * 1024;

永久生效要改配置文件。 Debian / Ubuntu 系在 /etc/mysql/mysql.conf.d/mysqld.cnf,CentOS 系在 /etc/my.cnf 的 [mysqld] 段:

[mysqld]
innodb_buffer_pool_size = 512M
innodb_buffer_pool_instances = 4

innodb_buffer_pool_instances 这行在 4GB 以下机器上不用改,默认值就够。 它的作用是把 buffer pool 切成多个实例以减少并发时的锁竞争,但每个实例都有自己的元数据开销,内存小的时候切得越碎越亏。

改完必须重启才能完全生效:

sudo systemctl restart mysql

重启会清空 buffer pool,前几分钟会明显变慢,这是正常的——数据要从磁盘重新读进内存。

验证改对了没有:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 应该输出 536870912(字节)

然后看命中率,这个数字比配置值更能说明问题:

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';

两个数相除,Innodb_buffer_pool_reads 除以 Innodb_buffer_pool_read_requests 就是磁盘读取的占比。 低于 1% 说明 buffer pool 很充裕;高于 10% 说明 buffer pool 不够用,该加了。如果 buffer pool 已经开到物理内存的一半以上还是上不去,那问题不是 buffer pool,而是你的查询没走索引——这时候加内存是纯浪费,该做的是 EXPLAIN。

一个常见误区:把 buffer pool 设成很大却发现没效果。 原因是 MySQL 5.7 之前 buffer pool 的数据页是 4KB 而实例是 8KB,命中率天然偏低。MySQL 8.0 改成了默认 16KB,命中率明显改善。所以如果你的应用还锁在 5.7,能升 MySQL 版本的话,升级带来的性能提升往往比调 buffer pool 更明显。

三、max_connections 该设多少

连接数是小内存 VPS 上第二个容易失控的参数,而且它的影响是乘性的。

先理解机制:每个连接在 MySQL 侧都要占内存,除了线程本身的栈,还有连接级缓冲(sort_buffer_size、join_buffer_size、read_buffer_size 的一部分)和会话临时表。 所以 max_connections = 500 不代表「最多 500 个用户」,而是「最多 500 份内存开销」。

计算公式:

连接数上限 = 留给 MySQL 的内存减去 buffer pool,再减去日志缓冲和运行开销,除以每连接的平均开销。

按每连接 2MB 算,几个常见配置:

1GB 机器:留 256MB 给 MySQL,buffer pool 128M(约 134MB),剩下约 100MB 给连接,除以 2MB 等于 50。 实际配置应该设 30 留余量。

2GB 机器:留 512MB,buffer pool 256M(约 268MB),剩 244MB 除以 2MB 等于 122。 实际配置应该设 60。

4GB 机器:留 1GB,buffer pool 768M(约 805MB),剩约 200MB 除以 2MB 等于 100。 实际配置应该设 80。

这些数字听起来比默认值小很多,但默认值 151 通常是在 8GB 机器上给的。

配置项在 [mysqld] 段:

max_connections = 60
max_connect_errors = 10000

max_connect_errors 要一起调。 默认值 100,意思是同一个客户端连续失败 100 次连接就被拉黑 10 分钟。 如果你的应用有连接池配置错误(密码错、连接串错),短时间内大量失败连接会把正常的客户端也一起封掉。调到 10000 是合理的安全余量。

设置连接数上限后,PHP 侧的连接池也要同步。 这是最容易被忽略的一步:MySQL 端设了 60,PHP 端如果 PDO 的 persistent connections 开着且不限制,连接会越积越多,最后把 MySQL 的 60 个位置占满,新来的请求直接拿不到连接。

PHP 侧要配的两处:

// 数据库连接配置里
'options' => [
    PDO::ATTR_PERSISTENT => false,   // 小内存机器上建议关闭持久连接
    PDO::ATTR_TIMEOUT => 5,
],

持久连接在小内存服务器上是有争议的优化:它省掉了反复握手的时间,但代价是连接常驻不释放。 对小机器来说,关掉持久连接,让 PHP-FPM 的 pm.max_requests 来控制连接生命周期,比开持久连接更可控。

WordPress 用户要注意: wp-config.php 里可以加这两行限制:

define( 'WP_MAX_MEMORY_LIMIT', '128M' );
define( 'WP_MEMORY_LIMIT', '128M' );

这两个值决定 WordPress 单次请求能用多少 PHP 内存。 超过就会报 Allowed memory size exhausted,但这不代表内存不够,往往只是这个值被设得太宽松了——128M 对绝大多数 WordPress 站点完全够用。

查当前连接数的使用情况:

SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
SHOW VARIABLES LIKE 'max_connections';

Max_used_connections 是历史峰值。 如果它接近你设的上限,说明该加了;如果长期只有 5 到 10,说明上限设太高了,可以降下来把内存还给 PHP。

一个必须知道的常见报错:

查 cloudcone 在售套餐与库存

ERROR 1040 (Too many connections) —— 连接数打满了。这个错的处理顺序是:先查 SHOW PROCESSLIST 看是谁在占连接(SHOW PROCESSLIST 里 Command 列是 Sleep 的就是空闲连接没释放),再决定是加连接数还是修应用的连接泄漏。 直接加连接数是治标——如果泄漏没修,加到 500 只会让内存爆得更彻底。

四、临时表和排序缓冲:最容易爆内存的两个参数

tmp_table_size 和 max_heap_table_size 是「每个连接」的内存上限,这是小内存机器上最隐蔽的内存黑洞。

机制是这样的:MySQL 处理 GROUP BY、ORDER BY、DISTINCT 时,如果数据量超过内存阈值,就把临时表写到磁盘(磁盘临时表)。 内存临时表的上限由 tmp_table_size 和 max_heap_table_size 两个值中较小的那个决定。所以这两个值必须设成一样大,否则改的那个不生效。

小内存机器上的推荐值:

tmp_table_size = 16M
max_heap_table_size = 16M

1GB 和 2GB 机器就用 16M。 4GB 机器可以用 32M。

但这里有个关键点:值设小不是坏事。 临时表写磁盘(Created_tmp_disk_tables 计数器会涨)确实比内存慢,但在小内存机器上,临时表写盘换来的稳定性远好过临时表吃内存导致 OOM。 正确做法是设小,同时优化查询让临时表根本用不上——加索引让 GROUP BY 和 ORDER BY 走索引,而不是全表排序。

验证临时表有没有溢出到磁盘:

SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';

两个数相除,如果 Created_tmp_disk_tables 占比超过 50%,说明大量临时表写到了磁盘。 这时候加内存是错的方向,该做的是给 ORDER BY 和 GROUP BY 的字段建索引。

sort_buffer_size 同理,是连接级别的:

sort_buffer_size = 256K

默认值通常就是 256K,在小内存机器上不用动。 要动的是 join_buffer_size(默认 256K)和 read_buffer_size(默认 128K 到 256K,全局级别),这两个也别乱调。

一句话总结这一节的调参哲学:小内存 VPS 上,所有「单连接级别」的内存参数都往低调,全局级别的按比例给。 因为单连接参数的实际占用是「连接数 × 参数值」,而全局参数只算一份。很多教程把这两个搞反了,在 2GB 机器上设 sort_buffer_size = 4M,100 个连接就是 400MB——一台 2GB 的机器直接玩完。

相关的报错原文:

ERROR 1114 (HY000): The table '/tmp/#sql_xxx' is full —— 临时表写满了,通常是磁盘空间不足或者 tmpdir 指向的分区满了。

ERROR 1030 (HY000): Got error 28 from storage engine —— 存储引擎报错,常见原因是磁盘满。

这两个错都指向磁盘,不是内存。 用 df -h 确认 /tmp 所在分区的剩余空间。

disk full 类错误的排查命令:

digitalocean 官网机型与实时报价

df -h
# 看 tmpdir 在哪
mysql -u root -p -e "SHOW VARIABLES LIKE 'tmpdir';"

五、binlog 和慢查询日志该开多大

日志是好事,但在小内存 VPS 上,日志写多了会吃掉磁盘,而磁盘写满会直接让 MySQL 崩溃退出。

先说结论:慢查询日志应该开着(而且应该常开),binlog 要看你有没有主从复制或定时备份需求。

慢查询日志的配置:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

long_query_time = 1 意思是记录执行超过 1 秒的查询。 默认值是 10 秒,太宽松了——10 秒以上的查询对你的用户来说已经是页面打不开了,但日志里什么都看不到。 1 秒是合适的起点。

log_queries_not_using_indexes = 1 这行价值很高,它记录所有没走索引的查询。 很多慢查询的根因就是缺索引,而这一行能让它们全部暴露出来。 代价是日志量会大一些,但对小内存机器来说,清洗日志比排查性能问题容易得多。

限制日志大小,避免撑爆磁盘。用 logrotate:

# /etc/logrotate.d/mysql-slow
/var/log/mysql/slow.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    postrotate
        systemctl reload mysql 2>/dev/null || true
    endscript
}

rotate 7 表示保留 7 天的历史。 copytruncate 让日志在不需要重启 MySQL 的情况下被截断,这一点对小内存机器很重要——重启 MySQL 意味着站点有几百毫秒到几秒的不可用。

binlog 的配置:

log_bin = /var/log/mysql/mysql-bin.log
binlog_expire_logs_seconds = 604800
max_binlog_size = 64M

binlog_expire_logs_seconds = 604800 是 7 天。 MySQL 8.0 用这个参数,5.7 用 expire_logs_days = 7。这个参数是防止 binlog 把磁盘写满的关键——MySQL 的 binlog 默认不会自动清理旧的日志文件,在一台 40GB 硬盘的小 VPS 上,binlog 长起来能把整个盘占满。

max_binlog_size = 64M 让单个 binlog 文件不超过 64MB,配合上面的过期时间,磁盘占用就可控了。

如果你不做主从复制也不做基于 binlog 的备份,就别开 binlog。 检查当前状态:

SHOW VARIABLES LIKE 'log_bin';

注意:关掉 binlog 之后,用 mysqldump 做的备份依然是完整的(mysqldump 是逻辑备份,不依赖 binlog),所以普通用户关掉 binlog 没有数据丢失风险。

一定要开的是慢查询日志。 它的成本只是磁盘,而收益是让你能回答「哪个查询最慢」这个几乎每天都要问的问题。

配合 MySQL 8.0 的 performance_schema 可以更精细地定位:

SELECT digest_text, count_star, avg_timer_wait/1000000000 AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC LIMIT 10;

查 hostwinds 在售套餐与库存

这个查询按平均耗时排序列出前 10 个最慢的 SQL 语句模板。 找到那条之后,再去建索引或者改写查询。

六、配置改了却没生效:怎么查

这是调参过程中最高频的困惑:明明改了配置文件,SHOW VARIABLES 出来的值还是老的。 这一节按可能性从高到低列出原因和排查顺序。

原因一:改错了配置文件。 这是最常见的一种。MySQL 会按固定顺序读配置文件,同一个参数在多个文件里出现时,后读的覆盖先读的。Debian / Ubuntu 系默认的读取顺序是:

mysql --help | grep -A 2 "Default options are read"

输出会列出所有会被读取的文件路径,按顺序排列。 用这条命令确认你改的文件确实在列表里。

然后找出到底哪个文件里有这个参数:

sudo grep -rn "innodb_buffer_pool_size" /etc/mysql/ /etc/my.cnf 2>/dev/null

如果搜出多个文件且数值不一样,那就是覆盖问题。记住后读的赢。

原因二:配置里有重复的 [mysqld] 段。 很多人在 mysqld.cnf 末尾追加配置时,没意识到文件里已经有一个 [mysqld] 段了。MySQL 对重复段是合并处理的(同名参数后一个赢),但如果两个段里改了不同的参数,就会出现「改了一个另一个没生效」的迷惑现象。 检查方法:

sudo grep -n "^\[" /etc/mysql/mysql.conf.d/mysqld.cnf

应该只看到一个 [mysqld]。

原因三:改的是别的服务的配置。 一些面板(宝塔、1Panel)有自己的 MySQL 配置模板,你在命令行改的 mysqld.cnf 可能被面板的启动脚本覆盖。判断方法:

sudo systemctl cat mysql | grep -i exec

看实际启动时读的是哪个配置文件。

原因四:改完忘了重启,或者重启失败了。 这是最朴素的错误,但也是最常见的。改配置后必须 sudo systemctl restart mysql,而且要确认重启成功了:

sudo systemctl restart mysql && systemctl is-active mysql

如果重启失败,MySQL 会起不来,站点直接挂掉。所以更安全的做法是先做配置语法检查:

sudo mysqld --validate-config

这条命令检查配置但不启动服务,语法有问题会直接报错。在小内存生产机上改 MySQL 配置之前跑这一条,能避免一次服务中断。

原因五:变量是动态的但你没重启,或者它本来就是只读的。 MySQL 的变量分三类:静态变量(改配置文件加重启才生效,比如 innodb_buffer_pool_size)、动态变量(可以 SET GLOBAL 立即生效,比如 max_connections)、会话变量(只影响当前连接)。查一个变量的来源:

SELECT VARIABLE_NAME, VARIABLE_SOURCE
FROM performance_schema.variables_info
WHERE VARIABLE_NAME = 'innodb_buffer_pool_size';

VARIABLE_SOURCE 显示为 COMPILE 就说明它是编译时确定的,只能改配置文件加重启。

原因六:改的值被四舍五入或单位搞错。 buffer pool 的值只能按 1024 的整数倍设置,设成 500M 会被向上取整到 512M。所以你设 500M 看到的是 536870912 字节(正好 512MB),这是正常的,不是没生效。

排查的完整顺序,一次走一遍:

# 1) 确认参数当前值
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# 2) 确认配置文件被读到了
sudo grep -rn "innodb_buffer_pool_size" /etc/mysql/ /etc/my.cnf
# 3) 确认读取顺序
mysql --help | grep -A 2 "Default options are read"
# 4) 语法检查
sudo mysqld --validate-config
# 5) 重启并确认
sudo systemctl restart mysql && systemctl is-active mysql
# 6) 再查一次
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

查看 interserver 当前月价与库存

六步走完还没生效,那基本可以确定是配置文件被面板或者启动脚本覆盖了。

七、swap 到底该不该用

这个问题在 MySQL 社区里有明确的官方立场:小内存 VPS 上 MySQL 应该尽量避免 swap。 但实际情况往往需要 swap,所以正确做法是「知道它的代价,谨慎使用」。

官方建议避免 swap 的原因有两个,都很实在:

第一,MySQL 的 InnoDB 有一组内存结构是「内存里没有就出错」的,包括锁信息、线程栈。 一旦这些数据被换到磁盘上,MySQL 的响应延迟会出现数量级的恶化——从毫秒变成几百毫秒甚至秒级。

第二,swap 会让 OOM Killer 的判断失真。 内存不够时,内核本来会杀掉最大的进程(往往是 mysqld)来保护系统,加了 swap 之后内核会先疯狂换页,把磁盘 IO 打满,站点变得极慢但又不崩溃——这比直接崩溃更难排查。

所以推荐的策略是:给 MySQL 单独设一个很小的 swap 需求,靠调参让它永远用不到 swap,但保留 swap 作为「不会立刻 OOM」的最后一道保险。

具体的做法。 第一步,给机器加 swap(1GB 机器建议 1GB 到 2GB,2GB 以上建议 1GB 就够):

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

第二步,把 MySQL 的 innodb_buffer_pool_size 设成「物理内存减去 swap 再减去其他」的值。 换句话说,buffer pool 绝对不能大到需要动用 swap。

第三步,如果你的机器上还有其他吃内存的东西(比如 Elasticsearch、Redis),给 MySQL 单独划一个受限的 cgroup 容器(或者用 systemd 的 MemoryMax):

# /etc/systemd/system/mysql.service.d/limit.conf
[Service]
MemoryMax=800M

MemoryMax 的作用是:MySQL 用超过这个值就会被系统限制,触发 cgroup 内存压力而不是把整台机器拖垮。 对 2GB 机器上的 MySQL,设一个略小于机器内存的 MemoryMax 是最有效的保护手段——它让 MySQL 崩溃(可以被守护进程重启)而不是让整机失联。

怎么确认 swap 有没有被用上:

# 看 MySQL 进程的换页情况(vstat 里的 majflt 是主缺页次数)
sudo grep -E "pgfault|pgmajfault" /proc/$(pgrep -o mysqld)/stat

更简单的方式是看系统级的换页统计:

vmstat 1 5
# si 列是每秒换入,so 列是每秒换出

如果 si 和 so 长期不为 0,说明 swap 在被活跃使用,MySQL 的性能已经受影响了。 这时候该做的是降 buffer pool,而不是加 swap。

常见报错原文:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction —— 锁等待超时。这条经常和 swap 有关,因为进程被换到磁盘后,拿锁和放锁都变慢了。 另一个常见原因是长事务没提交。

查看 itldc 当前月价与库存

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction —— 死锁。InnoDB 会自动检测死锁并回滚其中一个事务,应用层需要做重试。

八、监控该盯哪几个指标

调参不能一劳永逸,得知道参数调完到底有没有效果。这一节给出要盯的指标和采集方法。

最重要的三个指标:

第一个是 InnoDB 缓冲池命中率。 计算方式是 1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests。低于 99% 说明 buffer pool 偏小。 MySQL 8.0 可以直接查:

SELECT
  (1 - (buffer_pool_reads / buffer_pool_read_requests)) * 100 AS hit_rate_pct
FROM (
  SELECT
    VARIABLE_VALUE AS buffer_pool_read_requests
  FROM performance_schema.global_status
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) r
CROSS JOIN (
  SELECT
    VARIABLE_VALUE AS buffer_pool_reads
  FROM performance_schema.global_status
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) b;

第二个是连接数使用率。 Threads_connected 除以 max_connections。超过 80% 就要警惕了,因为突发流量会直接撞上限。

第三个是临时表落盘率。 Created_tmp_disk_tables 除以 Created_tmp_tables。超过 50% 说明有查询在用磁盘临时表,该加索引了。

把这三个指标做成一个监控脚本,用 cron 每分钟采集一次:

#!/bin/bash
# /usr/local/bin/mysql-monitor.sh
MYSQL="mysql -u root -pYOURPASS -N -B -e"

$MYSQL "SHOW GLOBAL STATUS WHERE Variable_name IN
('Threads_connected','Threads_running','Created_tmp_tables','Created_tmp_disk_tables',
 'Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests','Uptime');" \
| awk -F'\t' '
{ v[$1]=$2 }
END {
  hit = (v["Innodb_buffer_pool_read_requests"]>0) ? (1 - v["Innodb_buffer_pool_reads"]/v["Innodb_buffer_pool_read_requests"])*100 : 0
  tmp = (v["Created_tmp_tables"]>0) ? v["Created_tmp_disk_tables"]/v["Created_tmp_tables"]*100 : 0
  printf "%s conn=%d running=%d bufhit=%.2f%% tmpdisk=%.1f%%\n",
    strftime("%Y-%m-%d %H:%M:%S"), v["Threads_connected"], v["Threads_running"], hit, tmp
}'

采集结果写进日志,配合日志轮转控制磁盘占用:

*/1 * * * * /usr/local/bin/mysql-monitor.sh >> /var/log/mysql/monitor.log 2>&1

注意脚本里的密码是明文的,这是一个安全问题。 更稳妥的做法是用 ~/.my.cnf 存凭证:

[client]
user=root
password=YOURPASS

然后把权限设成 600(chmod 600 ~/.my.cnf),脚本里就不用带密码了。

除了 MySQL 自己的指标,操作系统层面还有三个必看的:

# 1) 内存压力(这个最关键)
free -m
# Mem 行的 available 列要大于 Swap 行的 used

# 2) 磁盘 IO 等待
iostat -x 1 3
# %util 列接近 100 说明磁盘是瓶颈

# 3) IOPS 能力(现代 VPS 用的是虚拟化存储,IOPS 有限)
cat /sys/block/vda/queue/rotational
# 输出 0 表示是 SSD 或非旋转设备

关于 IOPS 这一条要展开说,因为它常被忽略:VPS 的「SSD」其实是虚拟磁盘,底层是共享的存储池。 你可以跑 fio 测出实际能拿到的 IOPS:

fio --name=test --filename=/tmp/fiotest --size=1G --rw=randread --bs=4k --iodepth=32 --direct=1 --time_based --runtime=30 --group_reporting

查 spinservers 在售套餐与库存

测出来的 IOPS 如果只有几百,那你的 buffer pool 就更该配大一点——因为从磁盘读的代价更大,缓存命中率每提升一个百分点收益都更明显。反过来,如果测出来是几万 IOPS,buffer pool 小一点影响也不大。

九、WordPress 和常见应用的具体调整

通用参数调好了,应用层还有各自的坑。

WordPress 特别耗内存,这是它的结构决定的。 一个没优化的 WordPress 站点,单次 PHP 请求可能吃掉 40MB 到 60MB 内存。如果你的机器上 WordPress 是唯一的应用,buffer pool 可以给小一点;PHP 那边要给足。

WordPress 必做的三件事:

第一,限制 PHP 内存(不是放大,是限制):

define( 'WP_MEMORY_LIMIT', '128M' );
define( 'WP_MAX_MEMORY_LIMIT', '256M' );

第二,关掉不需要的后台功能。 用 wp-config.php 里的常量禁用心跳、文件修改通知这些后台常驻进程:

define( 'DISABLE_WP_CRON', true );
define( 'WP_AUTO_UPDATE_CORE', false );

DISABLE_WP_CRON 特别值得说:WordPress 的 WP-Cron 是靠访问页面时触发的,高流量站点上它会频繁触发,每个触发都要加载整个 WordPress。关掉它然后改用系统 crontab 更可控:

*/15 * * * * cd /path/to/wordpress && wp cron event run --due-now

第三,装一个对象缓存。 这就是本文档的下一篇文章要讲的 Redis 的内容——但即使不用 Redis,WordPress 也有内置的对象缓存(transient),只是它存在数据库里而不是内存里。 装 Redis 之后,transient 会自动走 Redis,数据库的查询压力能降一大截。

Laravel 的调整点:

第一,关掉 debug。 APP_DEBUG=false,并且确保 .env 里的 APP_ENV=production。开发模式下 Laravel 会把完整错误信息写进日志,异常堆栈很长,在小内存机器上日志写入本身就很吃资源。

第二,配置队列的 worker 数。 php artisan queue:work --tries=3 --max-jobs=1000 --memory=128。--memory=128 是关键——超过 128MB 就退出重启,避免单个 worker 吃爆内存。 这和 PHP-FPM 的 pm.max_requests 是同一个思路。

第三,检查 session 的存储方式。 默认的 file session 会往磁盘写大量小文件,在 IOPS 有限的 VPS 上这是明显的性能损耗。改成 Redis 或数据库存储会好一些。

如果你的应用是自定义的 PHP 脚本,记住这一条:所有的查询都应该走索引。 在小内存 VPS 上,一次全表扫描比多占 1GB 内存更致命,因为前者是持续性的,后者可能只发生一次。

一个通用的排查动作:找出内存占用最高的三个查询。 用 MySQL 8.0 的 sys schema:

SELECT query, exec_count, total_latency, avg_latency,
       rows_sent_avg, rows_examined_avg, tmp_tables
FROM sys.statements_with_full_table_scans
ORDER BY avg_latency DESC
LIMIT 3;

查 justg 在售套餐与库存

rows_examined_avg 远大于 rows_sent_avg 的查询,就是缺索引的查询。 这个视图直接给出了答案,不用自己猜。

按你的情况,下一步该看哪家

上面讲的是判断标准,但多数人最后还是想问一句「那我该买谁」。按你的实际情况对照下面几条,直接去看对应的机型页比继续比价更快:

价格与库存以商家页面当日显示为准,下单前建议先确认退款窗口与是否支持按月付费。

十、配置文件模板与修改后的检查流程

把前面九节的内容汇总成一份可以直接抄的配置模板。

1GB 机器的模板:

[mysqld]
innodb_buffer_pool_size = 128M
innodb_log_buffer_size = 8M
max_connections = 30
max_connect_errors = 10000
tmp_table_size = 16M
max_heap_table_size = 16M
sort_buffer_size = 256K
join_buffer_size = 256K
read_buffer_size = 128K
table_open_cache = 400
thread_cache_size = 8
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

2GB 机器的模板:

[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_buffer_size = 16M
max_connections = 60
max_connect_errors = 10000
tmp_table_size = 16M
max_heap_table_size = 16M
sort_buffer_size = 256K
join_buffer_size = 256K
read_buffer_size = 128K
table_open_cache = 400
thread_cache_size = 8
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

4GB 机器的模板:

[mysqld]
innodb_buffer_pool_size = 768M
innodb_log_buffer_size = 32M
max_connections = 80
max_connect_errors = 10000
tmp_table_size = 32M
max_heap_table_size = 32M
sort_buffer_size = 512K
join_buffer_size = 512K
read_buffer_size = 256K
table_open_cache = 800
thread_cache_size = 16
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

这三份模板的差异遵循同一条规律:全局级别(buffer pool、日志缓冲)按内存比例给,连接级别(连接数、临时表、排序缓冲)往低调。

改完之后的检查流程,六步:

第一步,备份原配置。 sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /root/mysqld.cnf.bak。这一步花五秒钟,回滚时省一小时。

第二步,语法检查。 mysqld --validate-config 可以检查配置但不启动服务:

sudo mysqld --validate-config

第三步,重启并确认没有报错。 sudo systemctl restart mysql && systemctl status mysql。如果启动失败,先看日志:sudo journalctl -u mysql -n 50 --no-pager。

第四步,确认参数真的生效了。 这是最容易被跳过、也最容易出问题的一步——因为配置文件路径写错或者有重复的 [mysqld] 段,参数就不会生效。

SHOW VARIABLES WHERE Variable_name IN
('innodb_buffer_pool_size','max_connections','tmp_table_size','sort_buffer_size');

第五步,跑一遍业务验证。 打开你的站点,实际提交一次表单、登录一次、查一次列表。不要只看 systemctl status 显示 active 就认为没问题——参数错了 MySQL 照样能正常启动,只是性能不对。

第六步,观察 24 小时。 用第八节的监控脚本看三个指标有没有改善。如果 buffer pool 命中率没上去,先别急着加大,先确认查询走了索引。

回滚命令:

sudo cp /root/mysqld.cnf.bak /etc/mysql/mysql.conf.d/mysqld.cnf
sudo systemctl restart mysql

zenlayer 官网机型与实时报价

最后提醒一件事:调参的收益有上限,机器本身的配置有上限。 如果你在 2GB 机器上把所有参数都调到最优,数据库依然会因为内存不够而在高并发下变慢。这时候该做的是加内存或者减少并发,而不是继续调参数。 按公开商品参数库的价格,2GB 内存档位的月付机型在 16.67 美元到 29.99 美元之间,4GB 档位在 33.33 美元到 39.99 美元之间——具体报价以官网当日报价为准。

飞翔声明:飞翔网登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。

评论:0 次

阅读本文后您有什么感想? 已有 人给出评价!

  • 0 喜欢
  • 0 高兴
  • 0 鬼脸
  • 0 呵呵
  • 0 无聊
  • 0 伤心

相关新闻

网友评论

评分
★
★
★
★
★
力荐
选择头像:
10
999+人评分
★★★★★
★★★★
★★★
★★
★
查看更多 >

魔兽世界Dota式击杀音效插件PVPSound

点击进入 立即下载

关于飞翔 | 联系我们 | 大事记 | 下载帮助(?) | 广告联系 | 版权声明 | 网站地图 | 友情链接

Copyright 2010-2025 单机游戏下载 (R) 版权所有 飞翔下载所有游戏及软件下载资源来源互联网,并由网友上传分享。如有侵权,请来电来函告之。
飞翔忠告:抵制不良色情、反动、暴力游戏 合理安排游戏时间 享受健康生活 鄂公网安备:42011102003115号 【鄂ICP备17000818号-1】