MySQL 与 Redis 共用 Linux 服务器的资源优化配置
一、整体架构原则
┌─────────────────────────────────────────────┐
│ Linux Server (共享资源) │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ MySQL │ │ Redis │ │
│ │ (磁盘IO) │ │(内存为主) │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ┌────▼───────────────▼────┐ │
│ │ Linux Kernel │ │
│ │ - cgroups / cpuset │ │
│ │ - I/O Scheduler │ │
│ │ - Page Cache │ │
│ │ - Swap Management │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────┘
核心矛盾:MySQL 重度依赖磁盘 IO + 页缓存,Redis 重度依赖内存。两者共存时需在 内存分配、磁盘 IO 调度、CPU 隔离 三个维度做精细调优。
二、内存优化配置
1. 明确各自内存预算
# 查看当前内存使用
free -h
cat /proc/meminfo | grep -E "MemTotal|MemFree|Buffers|Cached"
2. MySQL 内存配置(my.cnf)
[mysqld]
# --- 关键内存参数 ---
# InnoDB Buffer Pool: 占总内存的 50%~70%
innodb_buffer_pool_size = 4G
# InnoDB Buffer Pool 实例数(大内存时提升并发)
innodb_buffer_pool_instances = 8
# Redo Log 大小(避免频繁刷盘)
innodb_log_file_size = 1G
innodb_log_buffer_size = 64M
# 连接相关内存(每个连接额外占用)
max_connections = 200
thread_cache_size = 16
# sort_buffer, join_buffer 等每连接参数要小!
sort_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 4M
join_buffer_size = 1M
# tmp_table_size 和 max_heap_table_size 限制临时表内存
tmp_table_size = 64M
max_heap_table_size = 64M
# Query Cache 已废弃,不要启用
query_cache_type = 0
query_cache_size = 0
3. Redis 内存配置(redis.conf)
# --- 关键内存参数 ---
# 最大内存限制(必须设置!防止 OOM)
maxmemory 2gb
# 淘汰策略(根据业务选择)
maxmemory-policy allkeys-lru
# 或: volatile-lru / allkeys-random / noeviction(生产慎用)
# LRU 采样精度
maxmemory-samples 10
# AOF vs RDB 内存影响
aof-use-rdb-preamble yes # 混合持久化,减少内存峰值
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 客户端输出缓冲区限制(防止慢客户端耗尽内存)
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# THP 透明大页禁用(见内核参数部分)
4. 内存压力监控与告警
# 创建监控脚本 /opt/monitor/memory_alert.sh
#!/bin/bash
TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}')
AVAIL_MEM=$(free -m | awk '/^Mem:/{print $7}')
USED_PERCENT=$(( (TOTAL_MEM - AVAIL_MEM) * 100 / TOTAL_MEM ))
if [ $USED_PERCENT -gt 90 ]; then
echo "$(date): Memory usage critical: ${USED_PERCENT}%" >> /var/log/memory_alert.log
# 可触发 kill 或通知
fi
# 添加 crontab 每分钟检查
* * * * * /opt/monitor/memory_alert.sh
三、磁盘 I/O 优化配置
1. I/O 调度器选择
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# SSD/NVMe → 推荐 none 或 mq-deadline
echo "none" > /sys/block/sda/queue/scheduler
# HDD → 推荐 deadline 或 bfq
echo "deadline" > /sys/block/sda/queue/scheduler
持久化配置(写入 /etc/default/grub 或 udev 规则):
# /etc/default/grub 中 GRUB_CMDLINE_LINUX 添加:
GRUB_CMDLINE_LINUX="elevator=deadline"
# 然后更新 grub
update-grub
reboot
2. MySQL InnoDB 磁盘 IO 优化
[mysqld]
# 双写缓冲与刷盘策略
innodb_flush_method = O_DIRECT # 绕过 OS page cache,避免双重缓存
innodb_flush_log_at_trx_commit = 1 # 强持久性;若性能优先可设为 2
# IOPS 密集型场景
innodb_io_capacity = 2000 # SSD 可设 5000+
innodb_io_capacity_max = 4000
# 预读机制
innodb_read_io_threads = 8
innodb_write_io_threads = 8
# 数据文件路径分散到不同物理盘(如果有多块盘)
# datadir=/data/mysql/data
# innodb_log_group_home_dir=/data/mysql/logs
3. Redis 磁盘 IO 优化
# Redis 主要写操作是 AOF/RDB,减少 IO 频率
save "" # 如果配合 AOF 可禁用 RDB save
appendonly yes # 启用 AOF
appendfsync everysec # 每秒刷盘,平衡性能与安全
no-appendfsync-on-rewrite no # 重写期间不阻塞 fsync
# 如果追求极致性能且可接受少量数据丢失:
# appendfsync always # 每次写都刷盘(极慢)
# appendfsync no # 由 OS 决定(最快但风险高)
4. 多磁盘分离方案(最优解)
# 假设: /dev/sda (SSD, 系统+MySQL), /dev/sdb (HDD, 备份/日志)
# MySQL 数据目录放 SSD
mkdir -p /data/mysql
chown mysql:mysql /data/mysql
# my.cnf: datadir=/data/mysql
# MySQL binlog 放独立盘(如果有)
# log_bin=/data/binlog/mysql-bin
# Redis dump 文件放另一盘
# dbfilename dump.rdb
# dir /data/redis/dump
四、CPU 与进程隔离
1. cgroups CPU 隔离
# 安装 cgroup tools
apt-get install cgroup-tools # Debian/Ubuntu
yum install libcgroup-tools # CentOS
# 创建 MySQL CPU 控制组
cgcreate -g cpu,memory:/mysql
cgset -r cpu.cfs_quota_us=50000 mysql # 限制 MySQL 最多用 50% CPU (2核机器)
cgset -r memory.limit_in_bytes=6G mysql # 限制 MySQL 最大内存 6G
# 创建 Redis CPU 控制组
cgcreate -g cpu,memory:/redis
cgset -r cpu.cfs_quota_us=50000 redis
cgset -r memory.limit_in_bytes=3G redis
# 启动服务时绑定 cgroup
cgexec -g cpu,memory:/mysql mysqld_safe
cgexec -g cpu,memory:/redis redis-server /etc/redis/redis.conf
2. NUMA 优化
# 查看 NUMA 拓扑
numactl --hardware
# MySQL 启动时绑定 NUMA 节点
numactl --cpunodebind=0 --membind=0 /usr/sbin/mysqld
# Redis 启动时绑定 NUMA 节点
numactl --interleave=all /usr/local/bin/redis-server /etc/redis/redis.conf
3. CPU 亲和性与线程绑定
# 查看 MySQL 线程 CPU 分布
top -H -p $(pgrep mysqld)
# 手动绑定关键线程(高级用法)
taskset -c 0,1 mysqld_safe # 将 MySQL 绑定到 CPU 0,1
taskset -c 2,3 redis-server # 将 Redis 绑定到 CPU 2,3
五、Linux 内核参数调优
1. /etc/sysctl.conf 核心参数
# ============================================
# 内存管理
# ============================================
# 降低 swappiness,避免 MySQL 页缓存被换出
vm.swappiness = 1
# 提高 inode 缓存效率
vm.vfs_cache_pressure = 50
# 透明大页(THP)对数据库有害,必须禁用
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 持久化 THP 禁用
cat > /etc/systemd/system/disable-thp.service << 'EOF'
[Unit]
Description=Disable Transparent Huge Pages
[Service]
ExecStart=/bin/sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled && echo never > /sys/kernel/mm/transparent_hugepage/defrag"
[Install]
WantedBy=multi-user.target
EOF
systemctl enable disable-thp.service
# ============================================
# 网络优化(Redis 高频短连接友好)
# ============================================
# TCP 快速回收
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
# TCP 窗口缩放
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# ============================================
# I/O 队列深度
# ============================================
# 增加 blk-mq 队列深度(SSD 适用)
echo 128 > /sys/block/sda/queue/nr_requests
echo 256 > /sys/block/sdb/queue/nr_requests
# ============================================
# 文件描述符限制
# ============================================
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
# sysctl 也需调整
fs.file-max = 100000
2. 应用层 ulimit
# /etc/security/limits.d/20-nofile.conf
mysql soft nofile 65535
mysql hard nofile 65535
redis soft nofile 65535
redis hard nofile 65535
# MySQL my.cnf 中确认
[mysqld]
open_files_limit = 65535
六、文件系统优化
1. 挂载选项
# ext4 推荐挂载选项
defaults,noatime,nodiratime,discard
# xfs 推荐挂载选项
defaults,noatime,nodiratime,allocsize=64k
# /etc/fstab 示例
/dev/sda1 /data/mysql ext4 defaults,noatime,nodiratime,discard 0 2
/dev/sdb1 /data/redis ext4 defaults,noatime,nodiratime 0 2
noatime避免每次读取文件都更新访问时间戳,显著减少不必要的磁盘写入。
2. 磁盘分区建议
推荐布局:
├── / (系统盘, SSD)
├── /data/mysql (MySQL data, binlog, 独立 SSD 分区)
├── /data/redis (Redis dump, AOF, 独立 SSD 分区)
└── /backup (备份, HDD 或远程存储)
七、监控与诊断工具
1. 实时监控脚本
#!/bin/bash
# /opt/monitor/mysql_redis_health.sh
echo "=== System Status ==="
uptime
free -h
df -h /data/mysql /data/redis
echo -e "n=== MySQL Status ==="
mysqladmin -u root -p${MYSQL_PASS} status 2>/dev/null || echo "MySQL not running"
mysql -u root -p${MYSQL_PASS} -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_%';" 2>/dev/null | head -5
echo -e "n=== Redis Status ==="
redis-cli info memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio"
redis-cli info stats | grep -E "instantaneous_ops_per_sec|rejected_connections"
echo -e "n=== Disk IO ==="
iostat -x 1 1 | grep -A 10 "^Device"
echo -e "n=== Load Average ==="
cat /proc/loadavg
2. 关键指标阈值
| 指标 | MySQL 警戒线 | Redis 警戒线 |
|---|---|---|
| CPU 使用率 | > 80% 持续 5min | > 80% 持续 5min |
| 内存使用率 | > 85% | > 90% |
| InnoDB Buffer Pool 命中率 | < 99% | N/A |
| Redis 内存碎片率 | N/A | > 1.5 需 MEMORY PURGE |
| 磁盘 IO 等待时间 | iowait > 20% | iowait > 20% |
| QPS/OPS 突降 | > 50% 下降 | > 50% 下降 |
| 连接数 | > max_connections * 0.8 | > maxclients * 0.8 |
八、故障排查清单
## 当出现性能问题时按以下顺序排查:
1. **内存不足?**
- `free -h` → 检查可用内存
- `vmstat 1` → 观察 si/so (swap in/out)
- 如果 swap 活跃 → 增大物理内存或缩小 MySQL buffer pool
2. **磁盘 IO 瓶颈?**
- `iostat -x 1` → 看 %util, await, svctm
- `%util > 80%` → 考虑加盘或优化查询
- `await > 10ms` (SSD) / `> 50ms` (HDD) → 异常
3. **CPU 争抢?**
- `top -H` → 看 MySQL 和 Redis 线程 CPU 分布
- `mpstat -P ALL 1` → 看各核负载均衡性
- 使用 cgroups 隔离
4. **锁竞争?**
- MySQL: `SHOW ENGINE INNODB STATUSG` → 看行锁等待
- Redis: `SLOWLOG GET 10` → 看慢命令
5. **网络问题?**
- `ss -s` → 看 TCP 连接状态
- `netstat -an | grep TIME_WAIT | wc -l` → TIME_WAIT 过多
九、终极建议:是否应该分离部署?
| 场景 | 建议 |
|---|---|
| 测试/开发环境 | ✅ 可以共用,做好上述优化即可 |
| 中小生产环境(QPS < 5000) | ⚠️ 可以共用,但需严格监控 |
| 大型生产环境(QPS > 10000) | ❌ 强烈建议分离部署 |
| 对延迟敏感(< 1ms) | ❌ 必须分离 |
根本原则:当 MySQL 和 Redis 在同一台服务器上时,任何一方的突发负载都会直接影响另一方。分离部署是最可靠的解决方案。如果因成本限制必须共用,请确保:
- 使用 cgroups 做资源隔离
- 设置合理的内存上限防止 OOM
- 启用实时监控系统
- 预留至少 30% 的资源余量
云知识CLOUD