在仅2核4GB内存的服务器上同时运行 MySQL 和 Redis,资源竞争非常激烈,需精细化协同优化。以下是兼顾稳定性、性能与安全的实战级优化方案(非理论建议):
🔍 一、前提诊断:先摸清真实负载
# 检查当前内存占用(重点关注 RSS)
ps aux --sort=-%mem | head -10
free -h && cat /proc/meminfo | grep -E "MemAvailable|SwapTotal"
# MySQL 内存实际使用(非配置值)
mysql -e "SHOW VARIABLES LIKE '%buffer%'; SHOW STATUS LIKE 'Threads_connected';"
# 计算 InnoDB 实际内存 = innodb_buffer_pool_size + key_buffer_size + max_connections*(sort_buffer_size+read_buffer_size+...)
# Redis 内存使用
redis-cli info memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio"
⚠️ 若
MemAvailable < 800MB或 Redismem_fragmentation_ratio > 1.5,必须立即干预。
🛠️ 二、硬性资源隔离(必做)
| 组件 | 限制方式 | 推荐值 | 说明 |
|---|---|---|---|
| MySQL | my.cnf 配置 |
innodb_buffer_pool_size = 1024Mmax_connections = 50sort_buffer_size = 256Ktmp_table_size = 32M |
缓冲池不超过总内存25%,避免OOM;连接数严格限制 |
| Redis | redis.conf 配置 |
maxmemory 1024mbmaxmemory-policy allkeys-lruhz 10lazyfree-lazy-eviction yes |
强制内存上限,启用惰性删除防卡顿 |
| 系统级 | systemd 资源控制 |
ini<br>[Service]<br>MemoryLimit=3G<br>CPUQuota=150%<br> |
防止单个服务吃光资源(需启用 cgroups v2) |
✅ 关键操作:
- 禁用 MySQL 的
query_cache_type=0(已废弃且耗CPU) - Redis 关闭
save ""(禁用RDB持久化),改用appendonly yes+aof-rewrite-incremental-fsync yes(减少AOF重写IO压力)
⚡ 三、协同优化策略(核心技巧)
1. 数据分层:让Redis真正减负
-- MySQL中避免全表缓存,只缓存高频热点
SELECT id, title, status FROM articles WHERE status='published' ORDER BY created_at DESC LIMIT 20;
-- ✅ 应用层将此结果存入Redis,设置TTL=300s
-- ❌ 不要缓存"SELECT * FROM users"这种全量数据
💡 原则:Redis只存「计算结果」或「聚合数据」,而非原始表镜像。
2. MySQL瘦身:释放内存给Redis
- 删除无用索引(
pt-duplicate-key-checker检测) - 将大文本字段(
TEXT/BLOB)迁移到外部存储(如MinIO),MySQL只存URL - 使用
TINYINT替代ENUM(节省内存且兼容性更好)
3. Redis精简:拒绝“内存黑洞”
# 定期清理无效key(避免过期key堆积)
redis-cli --scan --pattern "session:*" | xargs -r redis-cli del
# 监控大key(>10KB的Hash/List)
redis-cli --bigkeys
# 对大Hash拆分为 hash:key:part1, hash:key:part2...
4. 进程级协作:错峰执行
- MySQL 备份(
mysqldump)和 Redis AOF重写(BGREWRITEAOF)绝对禁止同时运行 - 设置定时任务错峰:
# /etc/crontab 0 2 * * * root /usr/bin/redis-cli BGREWRITEAOF # 凌晨2点 0 3 * * * root /usr/bin/mysqldump ... # 凌晨3点
📉 四、监控告警(防患于未然)
# 部署轻量级监控(无需Prometheus)
# /etc/cron.d/db-monitor
*/5 * * * * root bash -c 'echo "$(date): $(free -m | awk "NR==2{print $7}") MB free" >> /var/log/db-mem.log'
*/5 * * * * root bash -c 'echo "$(date): $(redis-cli info memory | grep used_memory_human | cut -d: -f2 | tr -d "[:space:]")" >> /var/log/redis-mem.log'
# 内存低于500MB时自动重启Redis(保MySQL)
if [ $(free -m | awk 'NR==2{print $7}') -lt 500 ]; then
systemctl restart redis
fi
🚫 五、必须规避的陷阱
| 错误做法 | 后果 | 正解 |
|---|---|---|
MySQL innodb_buffer_pool_size=2G |
Redis启动失败或OOM Killer杀进程 | 严格按公式:BufferPool ≤ 总内存×25% |
Redis maxmemory 不设限 |
内存爆满触发Linux OOM Killer随机杀进程 | 必须显式设置且≤1.2G(预留系统缓冲) |
| 同时开启MySQL慢查询日志 + Redis AOF + RDB | 磁盘IO 100%,CPU飙升 | 仅开AOF,关闭RDB和MySQL慢日志(用pt-query-digest按需分析) |
| 使用Redis存储Session但不设TTL | 内存持续增长直至崩溃 | SET session:abc "data" EX 1800(强制30分钟过期) |
✅ 最终效果验证指标
| 指标 | 健康阈值 | 检测命令 |
|---|---|---|
| 系统可用内存 | ≥ 800MB | free -m | awk 'NR==2{print $7}' |
| MySQL响应时间 | P95 < 150ms | mysqlslap --concurrency=10 --iterations=10 -q "SELECT ..." |
| Redis延迟 | redis-cli --latency -u redis://localhost:6379 < 5ms |
|
| CPU负载 | uptime | awk '{print $NF}' < 1.5 |
💡 终极建议:若业务增长,优先将Redis迁移至独立1C2G轻量云服务器(成本≈¥30/月),比硬扛更经济稳定。
需要我为你生成:
- 完整的
my.cnf和redis.conf适配版配置文件? - 自动化内存监控+告警脚本?
- MySQL索引优化检查SQL清单?
欢迎随时提出,可立即输出 👇
CLOUD技术博