速卖通素材
奋斗

2核4G云服务器部署MySQL 8.0后频繁OOM或卡顿,如何优化?

服务器

在 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';

持续监控:

  • 使用 tophtop 观察 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 » 2核4G云服务器部署MySQL 8.0后频繁OOM或卡顿,如何优化?