在仅 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 最后防线)
-
启动顺序与资源抢占
- 先启动 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
-
关键监控项(每5分钟检查) 工具 监控点 预警阈值 free -havailable< 300MB → 立即排查 cat /proc/meminfoMemAvailable< 256MB → 高危 dmesg -T | grep -i "killed process"OOM killer 日志 出现即失败! redis-cli info memorymem_fragmentation_ratio> 1.5 → 内存碎片高,考虑重启 mysqladmin statusThreads_connected> 25 → 检查应用连接泄漏 -
应急措施(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技术博