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

在 2GB内存的服务器上同时运行 MySQL 和 Redis 是一个极具挑战性的场景,因为两者都是内存密集型服务。如果配置不当,极易触发 Linux OOM(Out of Memory) killer导致服务崩溃。

以下是合理分配内存、避免 OOM 的系统化方案:


🔑 核心原则

  1. 预留系统内存:Linux 内核、文件系统缓存、进程开销等至少预留 512MB~768MB。
  2. MySQL 与 Redis 共享可用内存约 1.2GB~1.4GB。
  3. 设置内存上限:为 MySQL 和 Redis 显式限制最大内存使用量。
  4. 启用交换空间(Swap)作为最后防线(但性能较差,仅用于防崩溃)。
  5. 监控 + 自动重启机制:防止静默失败。

📊 推荐内存分配比例

组件 推荐最大内存 说明
系统预留 512MB ~ 768MB OS + buffer cache + 其他进程
Redis 256MB ~ 512MB 适合小数据集,开启 LRU/LFU 淘汰策略
MySQL 512MB ~ 768MB 重点控制 InnoDB Buffer Pool 大小
Swap 1GB ~ 2GB 作为安全垫,避免 OOM Killer 直接杀进程

✅ 典型配置示例:

  • 系统预留:600MB
  • Redis maxmemory: 512MB
  • MySQL innodb_buffer_pool_size: 512MB
  • Swap: 1GB

⚙️ 具体配置步骤

1. MySQL 配置优化(my.cnf 或 mysqld.cnf)

[mysqld]
# 限制 InnoDB 缓冲池大小(最关键!)
innodb_buffer_pool_size = 512M

# 减少其他内存消耗
innodb_log_file_size = 128M          # 默认值,可适当减小
innodb_flush_method = O_DIRECT       # 避免双重缓存
table_open_cache = 200               # 默认可能过高,调低
thread_cache_size = 8                # 根据并发调整
max_connections = 50                 # 限制最大连接数,避免线程栈内存爆炸

# 禁用不需要的功能
skip-name-resolve                    # 加快连接速度
performance_schema = OFF             # 关闭性能模式节省内存

💡 注意:innodb_buffer_pool_size 应占 MySQL 总内存的 70%~80%,其余留给 log、sort buffer、join buffer 等。


2. Redis 配置优化(redis.conf)

# 设置最大内存上限
maxmemory 512mb

# 设置淘汰策略(关键!)
maxmemory-policy allkeys-lru         # 或 volatile-lru / allkeys-lfu

# 持久化方式选择(牺牲持久性换内存)
save ""                              # 关闭 RDB 快照
appendonly no                        # 关闭 AOF(或设为 no-appendfsync-on-rewrite yes)

# 压缩对象以节省内存
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

# 禁用不必要的模块
loadmodule /path/to/module.so        # 不要加载非必要模块

💡 Redis 内存模型提示:

  • Redis 使用 SDS 字符串,小键值对开销大。
  • 使用 redis-cli --bigkeys 定期清理大 key。
  • 考虑使用 Redis Modules(如 ReJSON、RedisGraph)是否必要?非必要则禁用。

3. 创建 Swap 空间(安全垫)

# 检查当前 swap
free -h

# 创建 1GB swap 文件
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

# 调整 swappiness(建议 10~30,避免过度使用 swap)
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

⚠️ 警告:Swap 会严重降低性能,仅用于防止 OOM 杀死进程。生产环境应优先扩容内存而非依赖 Swap。


4. 监控与告警

使用工具监控内存使用:

# 实时查看内存
top -m memory

# 监控 MySQL 内存
mysqladmin extended-status -i1 | grep -E "Innodb_buffer_pool_pages_data|Threads_running"

# 监控 Redis 内存
redis-cli info memory | grep used_memory_human

# 监控 OOM 事件
dmesg | grep -i oom
journalctl -k | grep -i oom

推荐监控方案:

  • Prometheus + Grafana:部署 node_exporter 监控系统内存,mysql_exporter 和 redis_exporter 分别监控数据库。
  • 简单脚本告警:当内存使用 >85% 时发送通知。

5. 应用层优化(减少内存压力)

MySQL 侧:

  • 避免大查询(SELECT * → 只查必要字段)
  • 添加合适索引,减少临时表使用
  • 分库分表,缩小单库数据量
  • 定期 OPTIMIZE TABLE 清理碎片

Redis 侧:

  • 使用哈希结构代替多个独立 key
  • 设置过期时间(TTL),避免脏数据堆积
  • 定期删除无用 key
  • 使用 SCAN 替代 KEYS *(避免阻塞)

🧪 测试验证

  1. 压测模拟高负载:

    # MySQL 压测
    mysqlslap --concurrency=50 --iterations=100 --create-schema=testdb --query="SELECT * FROM users WHERE id=?;" --number-of-queries=10000
    
    # Redis 压测
    redis-benchmark -n 100000 -c 50 -d 100
  2. 观察内存曲线:

    • 确保 MySQL 和 Redis 内存使用稳定在设定上限以下。
    • 无频繁 Swap 使用。
    • 无 OOM 日志。
  3. 故障注入测试:

    • 手动增加数据量,观察淘汰策略是否生效。
    • 模拟突发流量,观察系统是否稳定。

🚨 极端情况应对

问题 解决方案
内存持续不足 升级服务器至 4GB+ 内存
Redis 频繁淘汰数据 检查是否有关键数据被误淘汰;改用 volatile-lru 保留有 TTL 的数据
MySQL 慢查询多 优化 SQL 和索引;考虑读写分离
Swap 使用率高 立即扩容内存;检查是否有内存泄漏
OOM 发生 检查 dmesg 日志;临时重启服务;长期需扩容

✅ 总结 checklist

  • [ ] 系统预留 ≥512MB
  • [ ] Redis maxmemory 设置为 256~512MB,启用 LRU/LFU 淘汰
  • [ ] MySQL innodb_buffer_pool_size 设置为 512~768MB
  • [ ] 创建 1GB Swap 作为安全垫
  • [ ] 设置 vm.swappiness=10
  • [ ] 安装 Prometheus + Grafana 监控
  • [ ] 定期清理 Redis 大 key 和 MySQL 碎片
  • [ ] 限制 MySQL max_connections 和 Redis 客户端连接数
  • [ ] 编写自动化脚本监控内存并告警

💡 最终建议:2GB 内存跑 MySQL + Redis 属于极限配置。如果业务增长,最可靠的方案是升级到 4GB 或更高内存的服务器。内存成本远低于因 OOM 导致的服务中断和数据丢失风险。

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