云计算百科
云计算领域专业知识百科平台

Linux 针对 MySQL 专用服务器的 OOM 预防策略配置

对于只运行 MySQL 的服务器,如果触发 OOM,无论怎样设置,数据库进程被杀死几乎是必然的。这是因为:

为什么 MySQL 总是首当其冲?

  • 内存占用最大
    在专用 MySQL 服务器上,MySQL 通常占用 80-99% 的物理内存(包括 InnoDB Buffer Pool、Key Buffer 等),其他进程(SSH、监控等)内存使用微不足道。

  • OOM Killer 的计算逻辑
    OOM Killer 基于 oom_score 选择目标,主要考虑因素包括:

    • 内存使用量(RSS):MySQL 通常最高
    • 进程运行时间:虽然长时间运行的进程有保护,但抵不过内存使用量的权重
    • 特权级:即使 MySQL 以 root 运行,保护作用有限
  • 过度分配导致的"完美风暴"
    MySQL 可能同时面临:

    • 自身 Buffer Pool 占用大量内存
    • 大量连接和临时表
    • 操作系统文件缓存也占用内存
    • Swap 空间不足或访问过慢
  • 即使设置了保护也可能失效

    # 常见的保护设置
    echo -1000 > /proc/$(pgrep mysqld)/oom_score_adj

    这种方法在混合负载服务器上有效,但在专用 MySQL 服务器上:

    • 当系统内存耗尽时,必须杀死某个进程
    • 其他进程太小,杀死它们释放的内存杯水车薪
    • 内核会"聪明地"忽略 oom_score_adj,选择能释放大量内存的进程
    • MySQL 成为唯一有效目标

    针对专用 MySQL 服务器的 OOM 预防策略

    1. 禁用 Overcommit(最关键的设置)

    # /etc/sysctl.conf 或 /etc/sysctl.d/99-mysql.conf
    vm.overcommit_memory = 2
    vm.overcommit_ratio = 95

    计算 CommitLimit = 物理内存 × overcommit_ratio/100 + Swap

    • 当申请内存超过此限制时,malloc() 直接返回失败
    • MySQL 会收到 ENOMEM 错误,而不是突然被杀死

    重要:需要配置 MySQL 正确处理内存分配失败。

    2. 合理配置 MySQL 内存使用

    # my.cnf 关键配置
    [mysqld]
    # InnoDB Buffer Pool – 不要超过物理内存的 70-80%
    innodb_buffer_pool_size = 物理内存的 70%

    # 限制全局和每个连接的内存使用
    max_connections = 合理值(如 200)
    # 每个连接的内存限制
    tmp_table_size = 32M
    max_heap_table_size = 32M
    join_buffer_size = 合理值
    sort_buffer_size = 合理值
    read_buffer_size = 合理值
    read_rnd_buffer_size = 合理值

    # 监控和限制
    performance_schema = ON

    3. 配置 Swap 但避免过度使用

    # 有一定 Swap 但不依赖它
    # 建议:物理内存的 25%-50%,但使用 SSD 交换分区
    vm.swappiness = 10 # 较低值,减少主动交换
    vm.vfs_cache_pressure = 100

    4. 使用 cgroups 硬性限制(推荐)

    # 创建 cgroup 限制 MySQL 总内存使用
    cgcreate -g memory:mysql
    # 设置内存限制为物理内存的 85%
    cgset -r memory.limit_in_bytes=85%物理内存 mysql
    cgset -r memory.memsw.limit_in_bytes=物理内存+适当Swap mysql

    # 将 MySQL 进程加入 cgroup
    cgclassify -g memory:mysql $(pgrep mysqld)

    或者 systemd 服务文件配置:

    [Service]
    MemoryMax=85% # 硬限制
    MemorySwapMax=10% # 限制 Swap 使用
    MemoryHigh=80% # 软限制,超过时开始回收

    5. 监控和预警系统

    — 监控 MySQL 内存使用
    SELECT * FROM sys.memory_global_by_current_bytes
    WHERE event_name NOT LIKE 'memory/performance_schema%'
    LIMIT 10;

    — 查看连接内存使用
    SELECT * FROM sys.memory_by_thread_by_current_bytes
    LIMIT 10;

    # 监控系统内存趋势
    cat > /usr/local/bin/memwatch.sh << 'EOF'
    #!/bin/bash
    COMMITTED=$(grep Committed_AS /proc/meminfo | awk '{print $2}')
    LIMIT=$(grep CommitLimit /proc/meminfo | awk '{print $2}')
    PERCENT=$((COMMITTED*100/LIMIT))
    echo "内存承诺使用率: ${PERCENT}%"
    if [ $PERCENT -gt 90 ]; then
    echo "警告: 内存接近过度分配极限!"
    # 触发警报
    fi
    EOF

    6. 配置 MySQL 内存分配失败处理

    # my.cnf
    [mysqld]
    # 使 MySQL 在内存不足时优雅降级
    innodb_use_sys_malloc = 0 # 使用自己的内存分配器
    # 定期检查内存使用
    performance_schema_max_memory_classes = 320

    7. 硬件和内核调优

    # 增加内存过量使用时的响应性
    vm.oom_dump_tasks = 1 # OOM时记录进程信息
    vm.panic_on_oom = 0 # 不要panic,让OOM killer工作
    vm.oom_kill_allocating_task = 0

    # 使用透明大页可能增加内存压力风险
    # 对于MySQL,通常建议关闭透明大页
    echo never > /sys/kernel/mm/transparent_hugepage/enabled

    当 OOM 不可避免时

    如果即使采取上述措施仍然可能触发 OOM,配置应急措施:

    1. OOM 后自动恢复

    # 在 /etc/systemd/system/mysql.service.d/oom-recovery.conf
    [Service]
    Restart=on-failure
    RestartSec=10
    OOMScoreAdjust=-1000

    2. 配置优先杀死辅助进程

    # 如果有监控或其他辅助进程,降低其优先级
    for pid in $(pgrep monitoring-agent); do
    echo 1000 > /proc/$pid/oom_score_adj
    done

    3. 使用内核压力 stall 信息

    # 监控内存压力早期预警
    echo 10000 > /proc/sys/vm/watermark_scale_factor

    总结

    对于专用 MySQL 服务器,防御 OOM 的关键顺序:

  • 首要:禁用 overcommit (vm.overcommit_memory=2)
  • 其次:合理配置 MySQL 内存参数,留足系统开销
  • 再次:使用 cgroups 硬限制 MySQL 总内存
  • 然后:配置适当的监控和预警
  • 最后:准备 OOM 发生后的恢复方案
  • 最根本的解决方案:确保物理内存足够,并有适当的余量(通常建议 MySQL 专用服务器上,InnoDB Buffer Pool 不超过物理内存的 75%)。

    这样配置后,即使内存不足,MySQL 会收到分配失败错误(可通过应用程序处理),而不是被突然杀死导致数据损坏或复制中断。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Linux 针对 MySQL 专用服务器的 OOM 预防策略配置
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!