在 2 核 4G(约 3.8GB 可用内存)的云服务器上部署 MySQL 8.0,默认配置极易触发 OOM(Out Of Memory),因为 MySQL 默认会尝试占用大量内存(如 innodb_buffer_pool_size 默认可能为物理内存的 50%~70%,加上其他组件很容易超支)。
以下是针对该配置的优化方案,按优先级排序:
1. 核心参数调优(最关键)
编辑 MySQL 配置文件(通常位于 /etc/my.cnf 或 /etc/mysql/my.cnf),在 [mysqld] 下添加或修改以下参数。
A. 限制 InnoDB 缓冲池大小
InnoDB Buffer Pool 是内存消耗的大户。在 4G 机器上,建议分配 1.5G ~ 2G 给缓冲池,预留空间给操作系统和其他进程。
[mysqld]
# 设置为 1.5G (推荐) 或 2G (如果业务主要是读操作)
innodb_buffer_pool_size = 1610612736 # 1.5G
# 或者简单写法:
# innodb_buffer_pool_size = 1.5G
# 如果数据量小于 1GB,也可以设为 1G
# innodb_buffer_pool_size = 1G
B. 关闭不必要的功能以节省内存
MySQL 8.0 默认开启了一些对小型服务器不需要的功能:
- 性能模式:关闭 Performance Schema 可显著减少内存(除非你需要深度诊断)。
- 连接数:限制最大连接数,避免并发过高导致内存爆炸。
- 临时表:将磁盘临时表阈值调高,但需配合调整内存临时表大小。
[mysqld]
# 关闭性能架构(大幅降低内存占用)
performance_schema = OFF
# 限制最大连接数(根据实际业务调整,4G 机器建议 100-200)
max_connections = 150
# 设置每个连接的缓冲区大小(关键!默认 16M 对于小内存机器太大)
# 计算方式:(总内存 - 缓冲池 - OS 预留) / max_connections
# 假设剩余 1G 用于连接缓冲,150 个连接,每个约 6MB
thread_stack = 256K
thread_cache_size = 10
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 2M
join_buffer_size = 2M
# 临时表配置:尽量使用内存临时表,避免频繁落盘
tmp_table_size = 32M
max_heap_table_size = 32M
# 日志相关:减小日志文件大小,防止写入时占用过多内存
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
C. 调整 Swap(虚拟内存)作为安全网
虽然 Swap 会降低性能,但在 OOM 场景下,它是防止数据库直接崩溃的最后防线。
# 检查是否已开启 swap
free -h
# 如果没有,创建一个 2G 的 swap 文件
dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 永久生效,添加到 /etc/fstab
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# 调整 Swappiness(让系统更倾向于使用物理内存,但在压力小时才用 Swap)
sysctl vm.swappiness=10
echo 'vm.swappiness=10' >> /etc/sysctl.conf
2. 查询与 SQL 优化
即使配置得当,低效的 SQL 依然会导致内存瞬间飙升。
- 禁止全表扫描:确保大表都有合适的索引。
- *避免 `SELECT `**:只查询需要的字段,减少网络传输和内存处理开销。
- 限制返回行数:在分页查询中,使用
LIMIT offset, size,避免OFFSET过大导致扫描大量无用行。 - 分析慢查询:
-- 开启慢查询日志 slow_query_log = 1 long_query_time = 2 -- 超过 2 秒的记录 log_output = FILE定期查看
/var/log/mysql/slow-query.log,针对耗时 SQL 添加索引或重写逻辑。
3. 操作系统层面的优化
-
关闭 SWAP 自动回收(可选):在某些极端情况下,Linux 可能会杀掉 MySQL 进程。确保
oom_score_adj设置合理,让系统优先杀死其他非关键进程而不是 MySQL。# 查看当前 oom_score cat /proc/<mysql_pid>/oom_score # 设置 MySQL 进程的 OOM 分数为负值(降低被杀概率) echo -100 > /proc/<mysql_pid>/oom_score_adj注意:这需要在 MySQL 启动脚本中执行,或者通过 systemd 服务配置。
-
清理缓存:定期监控
free -m,如果available很低且buff/cache很高,说明内存压力主要来自文件系统缓存,MySQL 本身可能还好。但如果available接近 0 且没有 swap 活动,则必须优化。
4. 监控与验证
配置完成后,务必重启 MySQL 并观察:
systemctl restart mysqld
检查配置是否生效:
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
mysql> SHOW VARIABLES LIKE 'max_connections';
mysql> SHOW VARIABLES LIKE 'thread_stack';
持续监控:
- 使用
top或htop观察RES内存占用,确保 MySQL 进程稳定在 3G 以内。 - 关注
dmesg | grep -i "out of memory",确认没有 OOM Killer 记录。 - 如果依然卡顿,检查是否有长时间运行的复杂事务未提交。
总结建议配置片段 (/etc/my.cnf)
[mysqld]
user = mysql
port = 3306
basedir = /usr
datadir = /var/lib/mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
bind-address = 0.0.0.0
# 核心内存控制
innodb_buffer_pool_size = 1.5G
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
performance_schema = OFF
# 连接与线程控制
max_connections = 150
thread_cache_size = 10
thread_stack = 256K
# 缓冲区优化
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 2M
join_buffer_size = 2M
# 临时表
tmp_table_size = 32M
max_heap_table_size = 32M
log_error = /var/log/mysql/error.log
slow_query_log = 1
long_query_time = 2
slow_query_log_file = /var/log/mysql/slow.log
最后提醒:如果经过上述优化后,业务负载依然经常达到 4G 上限,说明硬件资源确实不足。此时最经济的方案是升级云服务器的内存至 8G,或者将数据库迁移到专门的 RDS 实例,避免应用与数据库争抢资源。
轻量云Cloud