在运行 MySQL 8.0(尤其是生产环境)的 Linux 服务器上,swap 分区不必须物理删除或禁用,但强烈建议关闭 swap 或将其影响降至最低;而 vm.swappiness 的设置是关键调优手段之一。以下是专业、基于实践的详细说明:
✅ 结论先行:
| 项目 | 推荐做法 | 理由 |
|---|---|---|
| Swap 分区是否必须关闭? | ❌ 不强制“删除 swap”,✅ 但应确保 MySQL 进程几乎不被 swap(即:避免 OOM 杀死或严重延迟) | Swap 本身不是洪水猛兽,但 MySQL 对内存延迟极度敏感;swap-in/out 会导致查询卡顿、复制延迟、甚至主从断裂 |
vm.swappiness 设置 |
推荐值:1(或 0,见下文权衡)⚠️ 绝对避免 60(默认值)或更高 |
控制内核倾向于回收 page cache 还是交换匿名页(如 MySQL buffer pool)。低值可极大降低 MySQL 被 swap 的风险 |
🔍 为什么 MySQL 对 swap 敏感?
- MySQL 的
innodb_buffer_pool_size通常配置为物理内存的 50%–80%,是匿名内存(anonymous pages),属于可被 swap 的对象。 - 当系统内存压力大时,内核可能将部分 buffer pool 换出到 swap —— 单次 swap-in 延迟可达 10ms~100ms+(机械盘更糟),而 InnoDB 单次页读本应在 0.1ms 内完成。
- 表现为:慢查询暴增、
SHOW PROCESSLIST中大量Sending data/Copying to tmp table、复制延迟突增、Innodb_buffer_pool_wait_free上升。
💡 Oracle 官方文档 & Percona、MySQL AB 均明确建议:"Avoid swapping for MySQL servers"(MySQL 8.0 Reference Manual: Optimizing for InnoDB)
🛠️ 正确配置步骤(生产环境)
1️⃣ 检查当前 swap 和 swappiness
# 查看 swap 使用情况(重点关注 SWAP 列)
free -h
# 查看 swappiness 当前值
cat /proc/sys/vm/swappiness # 通常为 60
# 查看哪些进程被 swap(需 root)
grep -i swap /proc/*/status 2>/dev/null | awk '{print $1,$2,$3}' | sort -k3nr | head -10
2️⃣ 永久设置 vm.swappiness=1
# 临时生效(重启失效)
sudo sysctl vm.swappiness=1
# 永久生效:写入 sysctl.conf
echo 'vm.swappiness = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 验证
sysctl vm.swappiness
✅ 为什么是
1而非0?
swappiness=0:内核仅在内存完全耗尽(OOM)时才 swap,看似理想。- 但 Linux 内核 4.0+ 后,
swappiness=0仍可能 swap 匿名页(尤其在 memory cgroup 限制下),且会抑制 page cache 回收,导致其他应用(如文件服务)性能下降。swappiness=1是业界黄金折中:几乎不 swap 匿名页(MySQL buffer pool),同时允许内核在极端压力下安全回收 file cache(代价远小于 swap),避免 OOM killer 杀死 mysqld。
3️⃣ (可选但推荐)禁用 swap(若内存充足)
# 临时禁用所有 swap
sudo swapoff -a
# 永久禁用:注释 /etc/fstab 中 swap 行(或删除 UUID/设备行)
sudo sed -i '/swap/s/^/#/' /etc/fstab
# 验证
free -h # Swap 行应显示 0
⚠️ 注意:仅当 物理内存 ≥ MySQL 最大内存需求 + OS + 其他服务预留 ≥ 2GB 时才推荐完全禁用 swap。否则保留 swap 可作为 OOM 的最后缓冲(避免 mysqld 被直接 kill)。
4️⃣ 关键配套优化(同等重要!)
# 1. 锁定 MySQL 内存(防止 swap,需 CAP_IPC_LOCK 权限)
# 在 mysqld_safe 或 systemd service 中添加:
# --memlock # 或在 /etc/my.cnf 添加:
[mysqld]
memlock = 1
# 2. systemd 服务配置(推荐):
# /etc/systemd/system/mysqld.service.d/override.conf
[Service]
MemoryLock=true
LimitMEMLOCK=infinity
# 避免 systemd 自动重启掩盖 swap 问题
Restart=on-failure
RestartSec=30
5️⃣ 监控告警(必备!)
# 检查 MySQL 是否被 swap(每分钟执行)
awk '/^Name:/ {name=$2} /^MMUPageSize:/ {mmu=$2} /^MMUPageSize:/ && name=="mysqld" {print "mysqld swapped:", mmu}' /proc/[0-9]*/status 2>/dev/null | grep -v "0"
# 或使用工具:smem(按 swap 使用排序)
sudo smem -s swap -r | head -10
# Prometheus + node_exporter:监控 node_memory_SwapTotal_bytes, node_memory_SwapFree_bytes
# 告警规则:swap usage > 5% for 5m → 触发排查
📌 常见误区澄清
| 误区 | 事实 |
|---|---|
| “只要 swap 分区存在,MySQL 就一定会被 swap” | ❌ 错!swappiness=1 下,只要内存未严重不足,buffer pool 几乎不会被 swap。swap 存在 ≠ 被使用。 |
“swappiness=0 最安全” |
⚠️ 过时认知。现代内核中 0 可能引发 page cache 饥饿,且不解决所有 swap 场景;1 更平衡。 |
| “云服务器(如 AWS EC2)无需关心” | ❌ 错!云实例同样有 swap(如 EBS-backed AMI 默认启用),且 I/O 延迟更高,危害更大。 |
✅ 最终检查清单
- [ ]
vm.swappiness = 1(已持久化) - [ ]
free -h显示 swap 使用率 < 1%(或swapoff -a后为 0) - [ ]
mysqld进程的/proc/PID/status中MMUPageSize≈ 0(表示未被 swap) - [ ]
systemd配置了MemoryLock=true(如启用) - [ ] 监控覆盖 swap 使用率与 MySQL 内存指标
如需进一步优化,可结合:
- 使用 HugePages(对大 buffer_pool > 32GB 更有效)
- 调整
vm.vfs_cache_pressure(默认 100,可设为50加强 inode/dentry 缓存) - 使用
cgroups v2限制 MySQL 内存上限,避免争抢
需要我为你生成完整的 my.cnf 示例、systemd 配置模板,或自动化检查脚本吗?欢迎继续提问! 🐬
云服务器