在仅 2GB 总内存 的受限环境下(如低配云服务器、树莓派或嵌入式场景),MySQL 和 Redis 同时运行极易因内存不足导致 OOM(Out of Memory)、频繁 swap、服务崩溃或响应迟缓。以下是务实、可落地的优化方案,兼顾稳定性与基本可用性:
⚠️ 前提原则(必读)
- 不建议共存于 2GB 环境:MySQL + Redis + OS + 其他进程(SSH、日志等)极易超限。
✅ 强烈建议二选一:- 若需持久化结构化数据 → 优先保 MySQL,用轻量替代 Redis(如
SQLite或应用层缓存); - 若需高速缓存/会话存储 → 优先保 Redis,用 SQLite 或文件存储替代 MySQL。
- 若需持久化结构化数据 → 优先保 MySQL,用轻量替代 Redis(如
- 若必须共存 → 需严格限制两者内存上限,并禁用 swap(避免卡死)。
🔧 一、MySQL 优化(目标:常驻内存 ≤ 512MB)
✅ 关键配置(my.cnf 中 [mysqld] 段)
# === 内存相关 ===
# 总缓冲池控制在 256MB(InnoDB 主要内存消耗)
innodb_buffer_pool_size = 256M
# 禁用查询缓存(MySQL 8.0+ 已移除,5.7 建议关闭)
query_cache_type = 0
query_cache_size = 0
# 减小连接内存开销(按需调整,2G 下建议 ≤ 32 连接)
max_connections = 32
table_open_cache = 64
sort_buffer_size = 256K # 原默认 2M → 降低 8 倍
read_buffer_size = 128K
read_rnd_buffer_size = 128K
join_buffer_size = 256K
tmp_table_size = 16M
max_heap_table_size = 16M
# === InnoDB 优化 ===
innodb_log_file_size = 32M # 日志文件减小(原默认 48M+)
innodb_log_buffer_size = 2M
innodb_flush_log_at_trx_commit = 2 # 平衡安全与性能(非X_X场景可接受)
innodb_file_per_table = ON
innodb_flush_method = O_DIRECT # 避免 double buffering
# === 其他瘦身项 ===
skip_log_bin # 关闭 binlog(除非需要主从/恢复)
skip_performance_schema # 关闭性能监控(节省 ~100MB)
skip_ssl # 关闭 SSL(无 HTTPS 场景)
✅ 系统级配合
- 关闭 swap(防卡死):
sudo swapoff -a # 永久禁用:注释 /etc/fstab 中 swap 行 - 限制 MySQL 进程最大内存(cgroup v1 示例):
# 创建 cgroup 限制 MySQL 最多使用 600MB sudo cgcreate -g memory:/mysql echo 600000000 | sudo tee /sys/fs/cgroup/memory/mysql/memory.limit_in_bytes # 启动 mysqld 时加入 cgroup: sudo cgexec -g memory:mysql /usr/bin/mysqld --defaults-file=/etc/mysql/my.cnf
✅ 应用层配合
- 关闭长连接,用连接池(如
mysql-pool)复用连接; - 避免
SELECT *、大结果集分页(用游标分页); - 定期清理无用表、归档历史数据。
🧠 二、Redis 优化(目标:常驻内存 ≤ 384MB)
✅ 关键配置(redis.conf)
# === 内存限制(强制!否则可能吃光内存)===
maxmemory 384mb
maxmemory-policy allkeys-lru # 或 volatile-lru(若 key 有 TTL)
# === 禁用持久化(除非必须)===
save "" # 关闭 RDB 快照
appendonly no # 关闭 AOF(AOF rewrite 极耗内存)
# 若必须持久化 → 改为 AOF + everysec(但仍有风险)
# appendonly yes
# appendfsync everysec
# === 降低内存碎片 & 开销 ===
# 使用更省内存的数据结构
activerehashing yes
hz 10 # 降低定时任务频率(默认 10)
lazyfree-lazy-eviction yes # 删除过期 key 异步释放内存
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
# === 网络与连接 ===
tcp-keepalive 60 # 保持连接活跃,减少重连开销
timeout 300 # 自动断开闲置连接
maxclients 100 # 根据实际需求调低(默认 10000 太高)
✅ 进阶技巧(省下关键内存)
- 用
ziplist编码小集合(自动触发,无需配置):list-max-ziplist-size -2 # 默认已启用 hash-max-ziplist-entries 512 hash-max-ziplist-value 64 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 - 避免大 value:单个 value > 10KB 易引发内存碎片,拆分为多个 key;
- *用
SCAN替代 `KEYS `**(防阻塞); - 定期
MEMORY PURGE(Redis 4.0+)主动释放内存碎片。
📊 三、内存分配参考(2GB 总内存)
| 组件 | 推荐上限 | 说明 |
|---|---|---|
| OS + 基础服务 | 300MB | kernel、sshd、systemd、日志等 |
| MySQL | 512MB | Buffer pool + 连接缓冲等 |
| Redis | 384MB | maxmemory + Redis 进程自身开销 |
| 预留缓冲 | 300MB | 防止突发峰值、OOM Killer 触发 |
| 其他应用 | ≤ 512MB | PHP/Python/Nginx 等需严格限制 |
💡 实际运行后用
free -h和ps aux --sort=-%mem | head -10监控真实占用。
🚨 四、必须做的监控与兜底
- 启用 OOM Killer 日志:
dmesg -w | grep -i "killed process" - 基础监控脚本(每5分钟检查):
#!/bin/bash mem=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}') if [ $mem -gt 90 ]; then echo "$(date) CRITICAL: Memory usage ${mem}%" | logger -t mem-alert # 可选:重启 Redis 或 MySQL fi - 日志轮转:防止
/var/log占满磁盘(间接影响内存)。
✅ 替代方案(更稳妥的选择)
| 场景 | 推荐替代 | 优势 |
|---|---|---|
| 需要简单键值缓存 | SQLite + 内存模式 (PRAGMA journal_mode=MEMORY) |
零依赖、内存可控、ACID |
| 需要会话存储 | 文件系统存储(PHP session.save_handler = files) | 无额外进程、内存零占用 |
| 需要轻量关系数据库 | SQLite(单机、无服务) | 启动快、内存占用 < 10MB |
| 需要实时消息队列 | RabbitMQ lightweight config 或 NATS | 比 Redis 更省内存(特定场景) |
✅ 总结:2GB 环境黄金法则
| 项目 | 动作 |
|---|---|
| 绝不妥协 | maxmemory(Redis)和 innodb_buffer_pool_size(MySQL)必须硬限制 |
| 砍掉一切非必要 | 关闭 binlog、SSL、Performance Schema、Query Cache、AOF/RDB |
| 宁可慢,不可崩 | 降低并发、增大超时、用异步延迟写入(如 Redis 延迟双删) |
| 监控先行 | 不部署不监控,不监控不上线 |
| 终极建议 | 用 SQLite + 文件缓存 替代 MySQL+Redis 组合 —— 在 2GB 下最稳、最快、最省心 |
如需具体配置文件模板或一键检测脚本,我可为你生成。是否需要?
云小栈