问题

前几天遇到一个奇怪的问题,服务器内存明明够用,结果在对 MySQL 进行测压的时候却出现了 OOM,是 Linux 内核出错了吗?

具体现象如下:使用 sysbench 对 mysql 进行压测,并发 50、80 均正常输出,当并发达到 100 时开始报 OOM。

[root@BJDB-01 ~]# sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-host=BJDB-02 --mysql-port=3306 --mysql-user=root --mysql-password=*** --mysql-db=test --table-size=100000 --tables=5 --threads=100 --db-ps-mode=disable --auto_inc=off --report-interval=3 --max-requests=0 --time=360 --percentile=95 --skip_trx=off --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 --create_secondary=off run
sysbench 1.0.17 (using system LuaJIT 2.0.4)
 
Running the test with following options:
Number of threads: 100
Report intermediate results every 3 second(s)
Initializing random number generator from current time
 
Initializing worker threads...
 
 
FATAL: mysql_store_result() returned error 5(Out of memory (Needed 48944 bytes))
FATAL: 'thread_run' function failed: /usr/share/sysbench/oltp_common.lua:432: SQL error,errno = 5, state = 'HY000': Out of memory (Needed 48944 bytes)
FATAL: mysql_store_result() returned error 5(Out of memory (Needed 48944 bytes))
FATAL: 'thread_run' function failed: /usr/share/sysbench/oltp_common.lua:432: SQL error,errno = 5, state = 'HY000': Out of memory (Needed 48944 bytes)

MySQL 中 error log 的报错为:

······
2021-03-16T09:13:00.692622+08:00 343 [ERROR] [MY-010934] [Server] Out of memory (Needed 708628 bytes)
2021-03-16T09:13:00.692702+08:00 343 [ERROR] [MY-010934] [Server] Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space
2021-03-16T09:13:59.832037+08:00 374 [ERROR] [MY-000000] [InnoDB] InnoDB: Assertion failure: ut0ut.cc:678:!m_fatal
InnoDB: thread 140375101384448
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/refman/8.0/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
01:13:59 UTC - mysqld got signal 6 ;

分析

对 MySQL OOM 我们一步步进行分析,首先应该查看 ulimit 限制,

[root@BJDB-02 ~]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 23045
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 23045
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

系统并没有对 ulimit 进行限制,100 个并发量在我们的配置之内,那就不是 ulimit 配置的问题,接下来分析下内存的使用情况,

在复现 MySQL OOM 的过程中,查看对应内存使用,通过 top 和 free 命令进行监控,得到以下信息,

[root@BJDB-02 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:          16047        1958        8956         8        5132       13920
Swap:          5119           0        5119
  
[root@BJDB-02 ~]# top
top - 17:21:30 up 32 min,  4 users,  load average: 0.00, 0.04, 0.11
Tasks: 226 total,   2 running, 224 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.2 us,  0.6 sy,  0.0 ni, 97.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  16432236 total,  9164596 free,  2012156 used,   5255484 buff/cache
KiB Swap:        5242876 total,    5242876 free,        0 used.  14247508 avail Mem
 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 7654 root      20   0   13.7g   1.7g  14312 S   1.3 10.3   0:00.27 mysqld

这里看我们的内存使用很正常,free 和 available 都很多,swap 都没有使用,唯一存在异常的是虚拟内存有点高,我们接着分析:

进一步查看一下 /proc/meminfo,具体分析一下内存的使用情况,其中以下两个参数引起了注意:

[root@BJDB-02 ~]# cat /proc/meminfo | grep Commit
CommitLimit:     13458992 kB
Committed_AS:    13244484 kB

一般来说 CommitLimit 的值是要比 Committed_AS 的值要小的,结合现在内存的使用,我们应该注意到一个 OS kernel 参数。

[root@BJDB-02 ~]# cat /etc/sysctl.conf |grep vm.overcommit_memory
vm.overcommit_memory=2

将 vm.overcommit_memory 调整为 0,压测时 MySQL OOM 消失了。

这三个参数值是什么意思呢?它和内存使用的关系是什么?内存真的够用吗?通过翻看 Linux 的内核文档我们来进行详细说明。

分析 vm.overcommit_memory 的使用

首先解释下 overcommit 的意思是指操作系统承诺给进程的内存大小超过了实际可用的内存。

从内核版本 2.5.30 开始,这个参数的解释为:

overcommit_memory:This value contains a flag that enables memory overcommitment.

  • When this flag is 0, Heuristic overcommit handling, the kernel attempts to estimate the amount of free memory left when userspace requests more memory. Obvious overcommits of address space are refused. Used for a typical system. It ensures a seriously wild allocation fails while allowing overcommit to reduce swap usage. root is allowed to allocate slightly more memory in this mode. This is the default.

  • When this flag is 1, Always overcommit, the kernel pretends there is always enough memory until it actually runs out. Appropriate for some scientific applications. Classic example is code using sparse arrays and just relying on the virtual memory consisting almost entirely of zero pages.

  • When this flag is 2, Don't overcommit, the kernel uses a "never overcommit" policy that attempts to prevent any overcommit of memory. The total address space commit for the system is not permitted to exceed swap + a configurable amount (default is 50%) of physical RAM. Depending on the amount you use, in most situations this means a process will not be killed while accessing pages but will receive errors on memory allocation as appropriate.

中文释义:

  • 当这个标志为 0 时,表示试探性的 overcommit,当用户空间请求更多内存时,OS kernel 会预估剩余的空闲内存量,如果内存申请特别大就会被拒绝。例如 malloc() 一次性申请的内存大小就超过了 swap 和 physical RAM 的和,就会遭到 kernel 拒绝 overcommit。

  • 当这个标志为 1 时,kernel 会假装一直有足够的内存,直到实际用完为止。

  • 当这个标志为 2 时,kernel 使用“永不过度提交”的策略,试图阻止任何内存的过度提交。


从含义中分析,如果我们将 vm.overcommit_memory 的值设为 2,就很有可能出现内存申请的值超过我们的阈值,就会受到禁止。该阈值是通过内核参数 vm.overcommit_ratio 或 vm.overcommit_kbytes 间接设置的,从对应参数解释中得到公式如下:

CommitLimit = Physical RAM * vm.overcommit_ratio + Swap                  // vm.overcommit_ratio 是内核参数,默认值是 50,表示物理内存的 50%。

测试环境中 Physical RAM 的值约为 16G,Swap 的值约为 5G,计算下来可正对应 CommitLimit 的值 13G。

/proc/meminfo 中的 Committed_AS 表示所有进程已经申请的内存总大小,而我们查询的 free 和 top 下的内存则是进程已经分配的内存。

Committed_AS 是 OS kernel 对所有进程在最坏情况下需要多少 RAM/swap 的预估,才能保证工作负载不会出现 OOM,因此会存在过度申请提交内存的现象。

这个值是系统所有运行的程序所申请的内存大小,并不代表着分配使用的大小,而且各个程序申请的内存是可共享的。

虽然 Committed_AS 的数值与虚拟内存 VIRT 的大小很相似,目前没有找到官方说明他们之间的联系,经过多次测试,它的大小和虚拟内存并没有关系。

总结

如果 Committed_AS 超过 CommitLimit 就表示发生了overcommit,超出越多表示 overcommit 越严重,kernel 的 killer 进程会挑一部分进程干掉,如果内存不够还会继续被 killer 干掉,MySQL 在内存使用中占用最大,首当其冲。

测试环境查看 Committed_AS 和 CommitLimit 的参数值为:CommitLimit: 13458992 kBCommitted_AS: 13244484 kB。两者已经十分接近,在vm.overcommit_memory=2 的情况下非常容易发生 MySQL 的 OOM,因此需要将 vm.overcommit_memory 的值设为 0,具体需要根据环境变更。

附:还有两个参数与我们这次的内存分配有关系,不过影响不大,有兴趣的同学可以自行谷歌:admin_reserve_kbytes 和 user_reserve_kbytes。

转:https://mp.weixin.qq.com/s/w60rnbA5b2gOdxtKLwdmOw

MySQL OOM的更多相关文章

  1. mysql oom之后的page 447 log sequence number 292344272 is in the future

    mysql oom之后,重启时发生130517 16:00:10 InnoDB: Error: page 447 log sequence number 292344272InnoDB: is in ...

  2. MySQL 5.7 GTID OOM bug案例分析 --大量压测后主从不同步

    转载自:http://www.sohu.com/a/231766385_487483 MySQL 5.7是十年内最为经典的版本,这个观点区区已经表示过很多次.然而,经典也是由不断地迭代所打造的传奇.5 ...

  3. 5.6 太多分区引起OOM

    一个月之前,Scott和同事们发现公司有一个MySQL MHA集群的master(假设master机器名为hostA)每隔一周左右就会挂一次(指MySQL挂掉),在几周内,MHA来回切了好几次. 按照 ...

  4. MySQL优化小结

    数据库的配置是基础.SQL优化最重要(贯穿始终,每日必做),由图可知,越往上优化的面越小,最基本的SQL优化是最重要的,往上各个参数也没太多调的,也不可能说调一个innodb参数性能就会好多少,而动不 ...

  5. 解决MySQL Slave 触发 oom-killer

    最近经常有收到MySQL实例类似内存不足的报警信息,登陆到服务器上一看发现MySQL 吃掉了99%的内存,God ! 有时候没有及时处理,内核就会自己帮我们重启下MySQL,然后我们就可以看到 dme ...

  6. MySQL 5.6 OOM 问题解决分享【转】

    本文来自:杨德华的原创分享 | MySQL 5.6 OOM 问题解决分享 延伸阅读:Linux的内存回收和交换 当遇到应用程序OOM的时候,大多数时候只能用头疼来形容,应用程序还可以通过引流来临时重启 ...

  7. MySQL服务器发生OOM的案例分析

    [问题] 有一台MySQL5.6.21的服务器发生OOM,分析下来与多种因素有关 [分析过程] 1.服务器物理内存相对热点数据文件偏小,62G物理内存+8G的SWAP,数据文件大小约550G 触发OO ...

  8. (转)MySQL 5.6 OOM 问题解决分享

    本文来自:杨德华的原创分享 | MySQL 5.6 OOM 问题解决分享 原文:http://www.cnblogs.com/zhoujinyi/p/5763112.html 延伸阅读:Linux的内 ...

  9. Mysql中使用JDBC流式查询避免数据量过大导致OOM

    一.前言 java 中MySQL JDBC 封装了流式查询操作,通过设置几个参数,就可以避免一次返回数据过大导致 OOM. 二.如何使用 2.1 之前查询 public void selectData ...

随机推荐

  1. 【LeetCode】990. Satisfiability of Equality Equations 解题报告(C++ & python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS 并查集 日期 题目地址:https://le ...

  2. 【LeetCode】865. Smallest Subtree with all the Deepest Nodes 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  3. Windows11实现录屏直播,H5页面直播 HLS ,不依赖Flash

    这两天的一个小需求,需要实现桌面实时直播,前面讲了两种方式: 1.Windows 11实现录屏直播,搭建Nginx的rtmp服务 的方式需要依赖与Flash插件,使用场景有限 2.Windows 11 ...

  4. 如何在 Go 中将 []byte 转换为 io.Reader?

    原文链接: 如何在 Go 中将 []byte 转换为 io.Reader? 在 stackoverflow 上看到一个问题,题主进行了一个网络请求,接口返回的是 []byte.如果想要将其转换成 io ...

  5. 「算法笔记」2-SAT 问题

    一.定义 k-SAT(Satisfiability)问题的形式如下: 有 \(n\) 个 01 变量 \(x_1,x_2,\cdots,x_n\),另有 \(m\) 个变量取值需要满足的限制. 每个限 ...

  6. docker-部署jumpserver

    jumpserver https://jumpserver.org/ Docker 部署 jumpserver 堡垒机 容器部署 jumpserver-1.4.10 服务端 #最好单一个节点 容器运行 ...

  7. uniapp使用uni.openDocument打开文件时,安卓打开成功,iOS打开失败【原因:打开的文件的文件名是中文】

    解决办法:使用escape进行文件名编码 uni.downloadFile({ url: url, success: function(res) { var filePath = res.tempFi ...

  8. CapstoneCS5210|CS5210低BOM成本方案CS5210|HDMI转VGA芯片方案

    Capstone最新推出的一款HDMI转VGA音视频转接线或者转换器方案芯片CS5210. 其设计的优势在于内置晶振,外围电路器件较少设计简单,芯片封装集成度较高,方案BOM成本低,相比其他方案产品更 ...

  9. 【优雅代码】03-optional杜绝空指针异常

    [优雅代码]03-optional杜绝空指针异常 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请顺 ...

  10. 基于Spring MVC + Spring + MyBatis的【图书资源管理系统】

    资源下载:https://download.csdn.net/download/weixin_44893902/45598347 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...