奋斗
努力

运行 MySQL 8.0 的服务器,swap 分区是否必须关闭?如何设置 vm.swappiness?

云计算

在运行 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/statusMMUPageSize ≈ 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 配置模板,或自动化检查脚本吗?欢迎继续提问! 🐬

未经允许不得转载:云服务器 » 运行 MySQL 8.0 的服务器,swap 分区是否必须关闭?如何设置 vm.swappiness?