2GB内存下MySQL和Redis如何合理分配内存避免OOM?

在仅 2GB 总内存 的受限环境中(如小型 VPS、嵌入式服务器或开发测试机),同时运行 MySQL 和 Redis 确实极易触发 OOM(Out of Memory)——尤其当系统未预留足够内存给 OS、其他进程(如 SSH、日志服务)及内核缓存时。以下是经过生产验证的、务实可行的内存分配与调优策略,目标是:稳定运行 + 避免 OOM + 可接受性能折衷。


✅ 一、基础原则(必须遵守)

项目 建议值 说明
OS 保留内存 ≥ 512MB Linux 内核、page cache、slab、SSH、systemd、日志等必需。低于 400MB 极易因 page cache 不足导致 I/O 飙升甚至 OOM killer 杀进程。
MySQL + Redis 总可用内存 ≤ 1.3GB(即 1331MB) 2048 - 512 = 1536MB 是理论上限,但需留出约 200MB 弹性缓冲(OOM 预警、突发负载、内存碎片),故保守设为 1300~1350MB。
禁止 swap 用于数据库 ❌ 关闭或严格限制 Swap 会极大拖慢 MySQL/Redis 响应(尤其是 Redis fork 或 MySQL buffer pool 刷盘),且可能触发 OOM killer。建议 swappiness=1 或 swapoff -a。

🔍 验证命令:

free -h        # 查看实际可用内存
cat /proc/sys/vm/swappiness  # 应 ≤ 1
ps aux --sort=-%mem | head -10  # 检查内存大户

✅ 二、MySQL 内存分配(推荐 ≤ 800MB)

MySQL 内存消耗主要来自 全局缓冲区(global) 和 线程级缓冲区(per-connection)。2GB 场景下必须禁用大内存特性,并严格限制连接数。

参数 推荐值 说明
innodb_buffer_pool_size 512M ~ 640M ⚠️ 核心!InnoDB 缓存表和索引数据。设为 640M(≈31% 总内存)已足够支撑小库(<1GB 数据)。绝不可 >768M。
key_buffer_size 16M MyISAM 缓存(若不用 MyISAM,设为 4M 或 0)
innodb_log_file_size 64M 日志文件大小(总和 ≤ 256M),避免过大导致恢复慢;配合 innodb_log_buffer_size=4M
max_connections 32 ~ 64 ⚠️ 关键!每个连接默认消耗 ~2~3MB(含 sort_buffer、join_buffer 等)。设 64 连接 * 2MB = 128MB,已很紧张。强烈建议设为 32。
sort_buffer_size 256K 禁止全局设大!按需在 SQL 中用 SET SESSION sort_buffer_size=... 临时调整
read_buffer_size / read_rnd_buffer_size / join_buffer_size 128K ~ 256K 全局设小,避免 per-connection 内存爆炸
tmp_table_size / max_heap_table_size 32M 防止内存临时表耗尽内存
innodb_buffer_pool_instances 4 提升并发访问效率(需 ≥4G 才设更高,此处 4 足够)

📌 my.cnf 示例(精简安全版):

[mysqld]
# 内存核心
innodb_buffer_pool_size = 640M
innodb_buffer_pool_instances = 4
max_connections = 32

# 连接与缓冲(严格控制)
sort_buffer_size = 256K
read_buffer_size = 128K
read_rnd_buffer_size = 128K
join_buffer_size = 256K
tmp_table_size = 32M
max_heap_table_size = 32M

# 日志与稳定性
innodb_log_file_size = 64M
innodb_log_buffer_size = 4M
innodb_flush_log_at_trx_commit = 1  # 安全优先(可接受性能损失)
sync_binlog = 1

# 其他
skip-log-bin
innodb_file_per_table = ON

✅ 启动后验证:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Threads_connected'; -- 实时监控连接数

✅ 三、Redis 内存分配(推荐 ≤ 512MB)

Redis 是纯内存数据库,必须设置硬性上限,否则 fork 子进程(RDB/AOF rewrite)时内存翻倍直接 OOM。

参数 推荐值 说明
maxmemory 448M ~ 480M ⚠️ 硬限制!预留 32~64MB 给 Redis 自身开销(复制缓冲区、AOF rewrite、client output buffers 等)。绝对不可设为 512M。
maxmemory-policy allkeys-lru 或 volatile-lru 推荐 allkeys-lru(所有 key 参与淘汰),除非你明确标记了 TTL 的缓存 key。避免 noeviction(OOM 风险高)。
tcp-backlog 511 减少连接队列积压
hz 10 降低定时任务 CPU 占用(默认 10,勿调高)
lazyfree-lazy-eviction yes 淘汰 key 时异步释放内存,避免阻塞
stop-writes-on-bgsave-error no RDB 失败时不中断写入(配合监控告警)
禁用 AOF appendonly no AOF rewrite 会 fork,内存峰值 ≈ 2× 当前数据量 → 在 2GB 下极其危险!生产环境如需持久化,仅用 RDB(save 900 1)。

📌 redis.conf 示例:

# 内存硬限(关键!)
maxmemory 460mb
maxmemory-policy allkeys-lru
lazyfree-lazy-eviction yes

# 禁用高风险功能
appendonly no
save 900 1      # 15分钟至少1次修改才触发 RDB
save 300 10     # 5分钟10次修改
save 60 10000   # 1分钟1w次修改(按需调整)

# 安全与资源
tcp-backlog 511
hz 10
stop-writes-on-bgsave-error no

✅ 启动后验证:

redis-cli info memory | grep -E "(used_memory|maxmemory|mem_fragmentation_ratio)"
# used_memory < maxmemory * 0.95 为健康(预留空间)

✅ 四、协同优化与监控(防 OOM 最后防线)

  1. 启动顺序与资源抢占

    • 先启动 MySQL(初始化 buffer pool 较慢),再启动 Redis,避免启动瞬间内存竞争。
    • 使用 systemd 设置内存限制(可选但强推):
      # /etc/systemd/system/mysqld.service.d/limit.conf
      [Service]
      MemoryLimit=800M
      # /etc/systemd/system/redis-server.service.d/limit.conf
      [Service]
      MemoryLimit=500M
  2. 关键监控项(每5分钟检查) 工具 监控点 预警阈值
    free -h available < 300MB → 立即排查
    cat /proc/meminfo MemAvailable < 256MB → 高危
    dmesg -T | grep -i "killed process" OOM killer 日志 出现即失败!
    redis-cli info memory mem_fragmentation_ratio > 1.5 → 内存碎片高,考虑重启
    mysqladmin status Threads_connected > 25 → 检查应用连接泄漏
  3. 应急措施(OOM 前)

    • echo 1 > /proc/sys/vm/oom_kill_disable ❌ 禁用 OOM killer 是错误做法! 正确做法是:
      ✅ 降级 Redis:redis-cli config set maxmemory 400mb
      ✅ 重启 MySQL(先 mysqladmin shutdown,再 systemctl start mysqld)
      ✅ 清理 Redis 无用 key:redis-cli --scan --pattern "temp:*" | xargs redis-cli del

✅ 五、终极建议(根据场景选择)

场景 推荐方案 理由
纯缓存(无持久化需求) Redis 512MB + MySQL 640MB Redis 设 maxmemory 480M,MySQL innodb_buffer_pool_size 640M,OS 保留 512M —— 极致利用,但需确保 Redis 无大 Value(单 key < 1MB)
需要 MySQL 高可靠性 MySQL 768MB + Redis 384MB MySQL buffer pool 加大提升查询性能,Redis 降配为轻量缓存层(如 session、短时效数据)
开发/测试环境 MySQL 512MB + Redis 384MB + 保留 1.1GB 给 OS/IDE/浏览器 开发机常开 Chrome、VS Code,内存更吃紧,宁可牺牲 DB 性能保系统响应
长期运行(>1个月) 每月重启一次 Redis 避免内存碎片累积(mem_fragmentation_ratio > 1.8 时必重启)

🚫 绝对禁止的操作(OOM 高发原因)

  • ❌ innodb_buffer_pool_size = 1G(超 OS 保留,OOM 必然)
  • ❌ maxmemory 512mb(无预留,fork 时 1G 内存需求 → OOM)
  • ❌ 同时开启 Redis AOF + RDB(rewrite 双重 fork)
  • ❌ MySQL max_connections = 200(200 × 2MB = 400MB+,仅连接就耗尽)
  • ❌ 不监控 MemAvailable,只看 free(Linux free 命令显示的 free 值不包含可回收 cache,极具误导性!)

如需进一步优化,可提供:

  • SHOW ENGINE INNODB STATUSG(MySQL 状态)
  • redis-cli info memory(Redis 内存详情)
  • free -h && cat /proc/meminfo(当前内存分布)
    我可为你定制化调优脚本或配置生成器。

💡 记住:在资源受限环境,“保守”不是妥协,而是稳定性的基石。
2GB 不是不能跑 MySQL+Redis,而是必须像嵌入式系统一样,每一 MB 都要精打细算。

未经允许不得转载:CLOUD技术博 » 2GB内存下MySQL和Redis如何合理分配内存避免OOM?