当 MySQL 和 Redis 共存于同一台 Linux 服务器时,二者对资源(尤其是内存和磁盘 I/O)存在显著竞争,若配置不当,极易引发性能抖动、OOM Killer 干预、慢查询激增或 Redis 频繁 swap。以下是生产环境推荐的系统级 + 应用级协同优化策略,兼顾稳定性、性能与可维护性:
🔑 一、核心原则(先决条件)
-
明确角色边界:
- ✅ MySQL:持久化、事务、复杂查询 → 依赖磁盘 I/O + 内存缓冲(InnoDB Buffer Pool)
- ✅ Redis:高速缓存/会话存储 → 纯内存操作,拒绝 swap,容忍有限持久化开销
→ 禁止让 Redis 与 MySQL 争抢关键资源
-
资源总量评估(必须做!)
free -h # 总内存 & 可用内存(注意 buff/cache) iostat -x 1 3 # 磁盘 %util, await, r/s w/s, avgqu-sz(识别瓶颈) df -h # 磁盘空间 & Inode 使用率(尤其 /var/lib/mysql 和 /var/lib/redis)
🧠 二、内存优化(最关键!防 OOM 和 Swap)
| 组件 | 关键配置 | 推荐值(示例:64GB 总内存) | 原因与说明 |
|---|---|---|---|
| Linux Kernel | vm.swappiness=1 |
echo 'vm.swappiness=1' >> /etc/sysctl.conf && sysctl -p |
⚠️ 严禁设为 0(会导致 OOM Killer 更激进),设为 1 表示仅在内存极度紧张时才 swap,保护 Redis |
| Redis | maxmemory + maxmemory-policy |
maxmemory 24gb(占总内存 ≤35%)maxmemory-policy allkeys-lru(避免 volatile-* 在无过期键时失效) |
✅ 强制内存上限,防止吃光内存 ❌ 禁用 noeviction(除非你确定绝不超限) |
| MySQL (InnoDB) | innodb_buffer_pool_size |
innodb_buffer_pool_size = 28g(≤45% 总内存,留足 OS 缓存 + Redis) |
💡 Buffer Pool 是 MySQL 最耗内存的部分,需预留至少 8~12GB 给 OS(文件缓存)、Redis、其他进程 |
| OS 层 | vm.vfs_cache_pressure=50 |
echo 'vm.vfs_cache_pressure=50' >> /etc/sysctl.conf |
降低内核回收 dentry/inode 缓存的倾向,提升文件系统性能(利好 MySQL 大量小文件场景) |
✅ 验证内存分配合理性:
# 检查实际使用(避免 buffer pool + redis + os cache 超过物理内存)
ps -eo pid,ppid,cmd,%mem --sort=-%mem | head -10
cat /proc/meminfo | grep -E "MemTotal|MemFree|Buffers|Cached|SReclaimable"
⚠️ 严重警告:
- 若 Redis 启用了
save或bgsave(RDB),fork 子进程时会复制页表(COW),瞬时内存需求 ≈ 当前 Redis 数据集大小!确保free -h显示的 "available" ≥ Redis maxmemory,否则 fork 失败导致 RDB 持久化中断。- MySQL 的
innodb_buffer_pool_size必须小于MemAvailable(非MemFree),否则启动失败。
💾 三、磁盘 I/O 优化(减少争抢,提升吞吐)
| 场景 | 优化措施 | 操作命令/配置 |
|---|---|---|
| 磁盘分离(强烈推荐) | 将 MySQL 数据目录(/var/lib/mysql)与 Redis 持久化目录(/var/lib/redis)放在不同物理磁盘/分区(如 SSD vs NVMe) |
bash<br>mkdir -p /mnt/ssd/mysql /mnt/nvme/redis<br>chown mysql:mysql /mnt/ssd/mysql<br>chown redis:redis /mnt/nvme/redis<br># 修改 my.cnf: datadir = /mnt/ssd/mysql<br># 修改 redis.conf: dir /mnt/nvme/redis |
| I/O 调度器优化 | 对 SSD/NVMe:none(禁用调度器);对 SATA HDD:deadline |
bash<br>echo 'none' > /sys/block/nvme0n1/queue/scheduler<br># 加入 /etc/rc.local 或 udev rule 持久化 |
| MySQL 日志优化 | 减少刷盘频率,但保证崩溃恢复能力 | ini<br># my.cnf<br>innodb_flush_log_at_trx_commit = 2 # 折中:每秒刷日志,非每次事务(牺牲极小安全性)<br>sync_binlog = 1000 # 每1000次事务同步 binlog(非 1)<br>innodb_io_capacity = 2000 # 根据 SSD 实测 IOPS 设置(如 NVMe 可设 5000+) |
| Redis 持久化策略 | 禁用 RDB + AOF 混合;优先选 appendonly yes + aof-use-rdb-preamble yes(Redis 7.0+),或仅用 AOF(appendfsync everysec) |
conf<br>save "" # 彻底禁用 RDB 触发式保存<br>appendonly yes<br>appendfsync everysec # 平衡性能与数据丢失风险<br>no-appendfsync-on-rewrite yes # BGREWRITEAOF 时不触发 fsync |
| 文件系统挂载选项 | 对 MySQL/Redis 数据盘启用 noatime,nobarrier(SSD)或 barrier=1(HDD) |
bash<br># /etc/fstab 示例(SSD)<br>/dev/nvme0n1p1 /mnt/nvme ext4 defaults,noatime,nobarrier 0 0 |
🔍 I/O 监控必备命令:
# 实时观察各进程 I/O(安装 iotop) iotop -o -P # 只显示实际 I/O 进程 # 查看磁盘队列深度(长期 >2 表示饱和) iostat -x 1 | grep -E "(nvme|sda).*await|avgqu-sz"
🛡 四、进阶防护与监控
-
cgroups v2 隔离(Linux 5.0+,强烈推荐)
限制 MySQL 和 Redis 的内存/CPU 上限,彻底避免互相影响:# 创建 memory cgroup sudo mkdir -p /sys/fs/cgroup/mysql /sys/fs/cgroup/redis echo "28G" | sudo tee /sys/fs/cgroup/mysql/memory.max echo "24G" | sudo tee /sys/fs/cgroup/redis/memory.max # 启动时绑定进程(systemd service 中添加) MemoryMax=24G # in redis.service [Service] section -
关键监控指标(接入 Prometheus + Grafana)
- MySQL:
Innodb_buffer_pool_wait_free,Threads_connected,Innodb_data_pending_reads/writes - Redis:
used_memory_rss,mem_fragmentation_ratio,evicted_keys,expired_keys,aof_last_rewrite_time_sec - 系统:
node_memory_MemAvailable_bytes,node_disk_io_time_seconds_total,node_vmstat_pgmajfault(大页缺页,预示 swap 风险)
- MySQL:
-
备份与维护错峰
- MySQL
mysqldump/xtrabackup安排在业务低谷,并设置ionice -c2 -n7降低 I/O 优先级 - Redis
BGSAVE/BGREWRITEAOF避免与 MySQL 备份同时运行
- MySQL
🚫 五、绝对禁止的配置
| 风险项 | 为什么危险 | 替代方案 |
|---|---|---|
vm.swappiness=0 |
内核无法 swap,OOM Killer 直接杀进程(常杀 Redis 或 MySQL) | 设为 1 |
Redis maxmemory 未设置 |
内存无限增长,挤占 MySQL 和 OS | 必须显式配置 |
MySQL innodb_buffer_pool_size > 50% 总内存 |
OS 缓存不足,MySQL 文件 I/O 性能暴跌 | ≤45%,留足给 OS + Redis |
Redis AOF appendfsync always |
每次写都刷盘,I/O 压力爆炸 | 改用 everysec |
| MySQL 和 Redis 共用同一块机械硬盘 | 随机读写(Redis)与顺序写(MySQL binlog)严重干扰 | 物理磁盘分离 |
✅ 总结:检查清单(部署前必做)
- [ ]
free -h确认available≥ Redismaxmemory+ MySQLinnodb_buffer_pool_size+ 12GB - [ ]
iostat -x 1确认磁盘%util < 70%,await < 10ms(SSD) - [ ]
/proc/sys/vm/swappiness=1 - [ ] MySQL
datadir和 Redisdir在不同物理设备上 - [ ] Redis
save ""(禁用 RDB)且appendonly yes+appendfsync everysec - [ ] MySQL
innodb_flush_log_at_trx_commit=2,sync_binlog=1000 - [ ] 配置 cgroups 或 systemd resource limits(内存硬限制)
💡 终极建议:单机共存是成本妥协方案。若业务增长,优先拆分为 MySQL 专用服务器 + Redis 专用服务器,这是最简单、最稳定的架构演进路径。
需要我为你生成对应的 my.cnf / redis.conf 完整模板(适配你具体的内存大小),或编写自动化检查脚本(Bash/Python),请随时告知!
云计算HECS