2GB 内存的服务器跑 MySQL,确实属于“小马拉大车”的边缘状态。在 CentOS 或 Ubuntu 上,如果默认配置直接启动,OOM(Out Of Memory)几乎是迟早的事,尤其是当并发连接数稍微上来一点,或者跑几个复杂查询的时候。
核心思路就四个字:极度保守。我们要把 MySQL 能用的内存压缩到极限,同时保证操作系统本身不崩。
以下是经过实战验证的调优方案,按优先级排序:
1. 首要任务:开启 Swap(虚拟内存)
这是防止 OOM 的第一道防线,也是救命稻草。虽然 Swap 速度慢,但比直接 Kill 进程要好得多。
检查是否已有 Swap:
free -h
如果没有 Swap 文件,必须创建。以 4GB 为例(给 MySQL 留足缓冲):
# 创建 4G 交换空间
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 永久生效,写入 fstab
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 调整 swappiness 参数,让系统更倾向于使用物理内存,但在内存紧张时才用 Swap
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
注意:不要设
swappiness=0,那会导致系统在内存耗尽前拒绝分配新内存,反而容易触发 OOM。10-30是比较安全的区间。
2. MySQL 配置文件调优(my.cnf / mysqld.cnf)
在 2GB 机器上,绝对不要使用默认的 innodb_buffer_pool_size(通常是总内存的 50%-70%,即 1GB+)。你需要手动限制它。
编辑 /etc/my.cnf (CentOS) 或 /etc/mysql/mysql.conf.d/mysqld.cnf (Ubuntu),找到 [mysqld] 部分,添加或修改以下关键参数:
A. InnoDB 缓冲池(最关键)
[mysqld]
# 设置为 512M - 768M,留出足够空间给 OS 和其他进程
innodb_buffer_pool_size = 512M
# 如果是单实例且负载极低,最大不超过 1G
# innodb_buffer_pool_instances = 1 # 小内存建议设为 1,减少锁竞争开销
B. 连接数控制(防止线程爆炸)
每个 MySQL 连接都会占用一定的内存(主要是 read_buffer_size, sort_buffer_size 等)。默认 max_connections 可能很高,一旦并发起来,内存瞬间被撑爆。
# 根据实际业务需求设置,一般 Web 应用 100-200 足够
max_connections = 150
# 关键优化:大幅降低每个连接的非共享内存开销
# 这些是每个连接独立分配的,连接越多,消耗越大
read_buffer_size = 128K
read_rnd_buffer_size = 128K
sort_buffer_size = 256K
join_buffer_size = 256K
tmp_table_size = 32M
max_heap_table_size = 32M
解释:默认情况下,
sort_buffer_size和join_buffer_size可能是几 MB。对于 2GB 机器,这简直是灾难。降到几百 KB 甚至几十 KB,对大多数简单查询影响不大,但能显著节省内存。
C. 日志与临时表
# 关闭不必要的日志,减少 I/O 和内存压力
general_log = 0
slow_query_log = 1
long_query_time = 2
log_error = /var/log/mysql/error.log
# 限制临时表大小,避免磁盘溢出
tmpdir = /tmp # 确保 /tmp 有足够空间
D. 其他安全参数
# 允许本地回环访问更安全
bind-address = 127.0.0.1 # 如果只在本机访问数据库
# 禁用 DNS 解析,加快连接速度
skip-name-resolve
3. 操作系统层面优化
A. 关闭不必要的服务
2GB 内存很宝贵。SSH、Nginx/Apache、Redis、Docker 等都在抢资源。
- 停止非必要的后台服务:
systemctl stop <service_name> - 如果运行了 Docker,确保容器内的 MySQL 没有无限增长日志。
B. 调整内核参数
# 增加 inode 缓存等,提升小文件操作效率
sysctl -w fs.inotify.max_user_watches=524288
sysctl -w vm.overcommit_memory=1 # 允许超额分配内存,避免 OOM Killer 随机杀进程
4. 监控与预警
调优不是一劳永逸的。你需要知道什么时候该扩容或进一步调整。
推荐使用 Prometheus + Grafana 或者简单的脚本监控:
- MySQL 内存使用:重点关注
Innodb_buffer_pool_pages_dirty和线程数。 - 系统内存:关注
available而非free。 - Swap 使用率:如果 Swap 使用持续超过 50%,说明 MySQL 已经严重依赖虚拟内存,性能会急剧下降,需要升级硬件或优化 SQL。
5. 替代方案建议(终极解决方案)
如果经过上述调优,MySQL 依然频繁卡顿或 OOM,考虑以下路径:
- 迁移到 MariaDB 10.5+ 或 Percona Server:它们在某些场景下对内存管理更友好。
- 使用 SQLite:如果数据量小(< 10GB),读写并发不高,SQLite 是零配置、低内存的神器。
- 升级服务器:2GB 内存跑生产级 MySQL 本身就是高风险架构。升级到 4GB 是最具性价比的选择,成本增加不多,稳定性大幅提升。
- 使用云数据库 RDS:很多国内云厂商提供基础版 MySQL,自动处理内存隔离和备份,适合小项目。
总结 Checklist
| 项目 | 推荐值 | 说明 |
|---|---|---|
innodb_buffer_pool_size |
512M – 768M | 核心内存,不可过大 |
max_connections |
100 – 150 | 防止线程过多 |
sort_buffer_size |
256K | 单个连接缓冲区,极小化 |
join_buffer_size |
256K | 同上 |
tmp_table_size |
32M | 临时表上限 |
max_heap_table_size |
32M | 堆表上限 |
swappiness |
10 | 系统级 Swap 策略 |
skip-name-resolve |
ON | 提速连接,减少 DNS 查询 |
最后提醒:任何配置修改后,务必重启 MySQL 服务并观察至少 24 小时。生产环境操作前,请务必备份数据和配置文件。
云技术博客