在 MySQL 和 Redis 共用一台 Linux 服务器时,若未合理规划资源(尤其是磁盘 I/O 和内存),极易出现资源争抢、性能抖动、响应延迟飙升甚至服务不可用等问题。以下是基于生产实践的系统性优化配置建议,兼顾稳定性、性能与可维护性:
🔑 一、核心原则(先决条件)
- 不推荐共用生产环境:高负载场景下强烈建议物理/逻辑隔离(如容器+资源限制、或拆分到不同节点)。本文假设受限于成本/架构暂需共存。
- 监控先行:部署
iostat,vmstat,htop,redis-cli info memory,mysqladmin extended-status及 Prometheus + Grafana,明确瓶颈再调优。 - 避免“一刀切”配置:需根据实际 workload(读写比、数据量、QPS、延迟敏感度)动态调整。
💾 二、磁盘 I/O 优化策略
| 维度 | MySQL 优化 | Redis 优化 | 协同避坑 |
|---|---|---|---|
| 存储介质 | ✅ 强烈建议 MySQL 数据目录(datadir)、日志(innodb_log_group_home_dir, slow_query_log_file, general_log_file)挂载到 SSD/NVMe 分区;❌ 禁止与系统盘或 Redis 共用机械盘 |
✅ Redis 持久化(RDB/AOF)文件必须放在 独立 SSD 分区(如 /data/redis/);❌ 禁止与 MySQL 日志/数据混放(避免顺序写 vs 随机写冲突) |
⚠️ 关键:MySQL 的 InnoDB 日志(ib_logfile*)和 Redis 的 AOF 重写(BGREWRITEAOF)都是大块顺序写,若共用同一物理盘,会因磁头/队列争抢导致写延迟飙升(>100ms)→ 必须物理分离! |
| I/O 调度器 | ✅ MySQL(随机读写为主):echo deadline > /sys/block/nvme0n1/queue/scheduler(NVMe 用 none,SATA SSD 用 deadline 或 mq-deadline) |
✅ Redis(顺序写为主): 同上,但更推荐 none(NVMe)或 deadline(SATA SSD) |
✅ 统一设置:echo 'vm.swappiness = 1' >> /etc/sysctl.conf(降低 swap 倾向,避免 I/O 放大) |
| 文件系统 | ✅ XFS(推荐):支持大文件、延迟分配、noatime,nobarrier(若硬件有断电保护)❌ 避免 ext4(小文件多时元数据开销大) |
✅ XFS 同样适用; ✅ 挂载参数: noatime,nodiratime,logbufs=8,logbsize=256k(提升日志性能) |
✅ 统一挂载参数示例:mount -o noatime,nodiratime,barrier=1 /dev/nvme0n1p1 /var/lib/mysqlmount -o noatime,nodiratime,logbufs=8,logbsize=256k /dev/nvme0n1p2 /var/lib/redis |
| 持久化策略协同 | ✅ 关闭 innodb_flush_log_at_trx_commit=2(牺牲少量安全性换性能,配合电池缓存 RAID 卡)✅ sync_binlog=1000(降低 binlog 刷盘频率) |
✅ RDB:save 900 1(低频大快照)✅ AOF: appendfsync everysec(平衡安全与性能)❌ 禁用 always(每命令刷盘 → I/O 雪崩) |
⚠️ 禁止同时触发:避免 MySQL checkpoint + Redis AOF rewrite + RDB save 同时发生 → 用 cron 错峰(如 MySQL 备份在凌晨2点,Redis RDB 在凌晨3点) |
🧠 三、内存优化与隔离(重中之重!)
✅ 1. 内存总量分配(硬性约束)
- 总内存 = M(例如 64GB)
- Redis 内存上限:
maxmemory 32gb(redis.conf)
maxmemory-policy allkeys-lru(避免 OOM kill)
→ 预留至少 2GB 给 OS 缓存 + Redis 自身开销 - MySQL 内存上限:
# my.cnf innodb_buffer_pool_size = 24G # ≤ 总内存 × 0.4(若 Redis 占 32G,则 64×0.4=25.6G → 取24G) key_buffer_size = 32M # MyISAM 仅用于系统表,可忽略 sort_buffer_size = 2M # 按需调小,避免线程级内存爆炸 read_buffer_size = 1M join_buffer_size = 2M tmp_table_size = 64M max_heap_table_size = 64M - OS 预留:≥ 4GB(文件缓存、网络栈、内核页表等)
✅ 验证公式:
Redis_maxmemory + MySQL_innodb_buffer_pool_size + 4GB ≤ 总内存
✅ 2. 防止 OOM Killer 杀进程(生死线!)
# 为 Redis 进程降低 OOM 优先级(越负越不易被杀)
echo -500 > /proc/$(pgrep redis-server)/oom_score_adj
# 为 MySQL 进程设为中等优先级
echo -200 > /proc/$(pgrep mysqld)/oom_score_adj
# 永久生效(systemd 服务文件中添加)
# /etc/systemd/system/redis.service
[Service]
OOMScoreAdjust=-500
# /etc/systemd/system/mysqld.service
[Service]
OOMScoreAdjust=-200
✅ 3. 内存透明大页(THP)—— 必须禁用!
# 临时禁用
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 永久禁用(/etc/rc.local 或 systemd)
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local
echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/rc.local
❗原因:MySQL/Redis 的内存访问模式高度随机,THP 会导致内存碎片、延迟毛刺,实测 QPS 下降 20%+。
✅ 4. NUMA 优化(多路 CPU 服务器)
# 查看 NUMA 节点
numactl --hardware
# 启动 MySQL 时绑定到本地内存节点(避免跨 NUMA 访问)
numactl --cpunodebind=0 --membind=0 /usr/sbin/mysqld &
# Redis 同理(但 Redis 单线程,通常只需绑 CPU)
numactl --cpunodebind=1 redis-server /etc/redis.conf
🛠 四、进阶协同优化技巧
| 场景 | 方案 | 命令/配置 |
|---|---|---|
| 日志 I/O 隔离 | MySQL 的 slow_query_log、error_log 单独挂 SSD 小分区 |
slow_query_log_file = /ssd-logs/mysql-slow.log |
| Redis 不持久化? | 若业务允许(纯缓存),彻底关闭 RDB+AOF → 零磁盘 I/O | save "" + appendonly no |
| MySQL 压缩表 | 对历史归档表启用 ROW_FORMAT=COMPRESSED,减少 buffer pool 占用 |
ALTER TABLE t1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8; |
| 内核参数加固 | 减少 swap 使用、优化 TCP、文件句柄 | bash<br>echo 'vm.swappiness = 1' >> /etc/sysctl.conf<br>echo 'net.ipv4.tcp_tw_reuse = 1' >> /etc/sysctl.conf<br>echo '* soft nofile 65536' >> /etc/security/limits.conf<br> |
| 定时任务错峰 | 避免备份、清理、AOF rewrite 同时运行 | bash<br># MySQL 备份(凌晨2:00)<br>0 2 * * * /backup/mysql-backup.sh<br># Redis RDB(凌晨3:30)<br>30 3 * * * redis-cli bgsave<br> |
📉 五、必须规避的致命错误
- ❌
innodb_buffer_pool_size+maxmemory> 总内存 → 触发频繁 swap → I/O 雪崩 - ❌ Redis 使用
vm.overcommit_memory=1(默认)+maxmemory未设 → 内存超卖 → OOM Kill - ❌ MySQL 和 Redis 日志写入同一块 SATA 盘 → 随机写(InnoDB)与顺序写(AOF)互相干扰 → 平均延迟翻倍
- ❌ 忽略
ulimit -n(文件描述符)→ 连接数暴涨时大量Too many open files错误 - ❌ 未关闭 THP → Redis
INFO memory显示mem_allocator:jemalloc但仍有延迟毛刺
✅ 六、快速检查清单(部署后必做)
# 1. 内存是否超限?
free -h && cat /proc/meminfo | grep -E "MemAvailable|Buffers|Cached"
# 2. Redis 是否受 OOM 影响?
redis-cli info memory | grep -E "used_memory|maxmemory|mem_fragmentation_ratio"
# 3. MySQL Buffer Pool 命中率?
mysql -e "SHOW ENGINE INNODB STATUSG" | grep -A 5 "Buffer pool hit rate"
# 4. 磁盘 I/O 是否争抢?
iostat -x 1 5 | grep -E "(nvme|sda)" # 观察 %util, await, r_await, w_await
# 5. OOM Score 是否生效?
cat /proc/$(pgrep redis-server)/oom_score_adj # 应为 -500
cat /proc/$(pgrep mysqld)/oom_score_adj # 应为 -200
🌐 结语
共存不是不可以,但必须以“隔离”为设计前提,以“监控”为决策依据,以“渐进调优”为实施路径。建议:
- 第一阶段:按本文配置基础参数 + 错峰调度,跑 3 天压力测试(
sysbench+redis-benchmark); - 第二阶段:根据
iostat/vmstat数据微调innodb_io_capacity、redis maxmemory; - 第三阶段:长期观察
mem_fragmentation_ratio(Redis > 1.5 需重启)、Innodb_buffer_pool_wait_free(MySQL > 0 需增大 buffer pool)。
如仍有性能问题,终极方案:用 cgroups v2 严格限制 I/O bandwidth 和 memory usage(需 Linux 5.0+),但这已接近容器化管理范畴。
需要我为你生成 完整的 my.cnf + redis.conf 示例模板(含注释),或提供 cgroups 限速脚本,可随时告知 👇
云服务器