MySQL OOM(内存溢出)的解决思路

OOM全称"Out Of Memory",即内存溢出。

内存溢出已经是软件开发历史上存在了近40年的“老大难”问题。在操作系统上运行各种软件时,软件所需申请的内存远远超出了物理内存所承受的大小,就叫内存溢出。

内存溢出产生原因多种多样,当内存严重不足时,内核有两种选择:

  1. 直接panic
  2. 杀掉部分进程,释放一些内核。

大部分情况下,会杀掉导致OOM的进程,然后系统恢复。通常我们会添加对内存的监控报警,例如:当memory或swap使用超过90%时,触发报警通知,需要及时介入排查。

如果已经出现OOM,则可以通过dmesg命令查看,CentOS7版本以上支持 -T选项,能将时间戳转成时间格式,方便查看具体时间:

[root@localhost ~]# free -m    total  used  free  shared buffers  cachedMem:  128937  128527  409   1  166  1279-/+ buffers/cache:  127081  1855Swap:  16383  16252  131

通过日志可以看出哪些进程、占用多少内存等信息,并会Kill掉占用内存较大的进程。

内存问题的排查思路

一、操作系统内存检查

已MySQL为例,OOM后,mysqld进程被Killed,内存会被释放。mysqld_safe安全进程会将mysqld拉起,此时查看到的系统内存会是一个正常值。如果内存使用很高,但还未OOM,系统内存使用情况可能为下面情况:

[root@localhost ~]# free -m    total  used  free  shared buffers  cachedMem:  128937  128527  409   1  166  1279-/+ buffers/cache:  127081  1855Swap:  16383  16252  131

可以看出此时的内存使用已经很高了,物理内存和swap虚拟内存几乎都被用完,buffers和cached也不多,随时可能出现OOM的情况。

首先,通过top命名查看占用内存最大的进程:

shift+o可以选择排序方式,n代表%MEM。

[root@localhost ~]# topMem: 132031556k total, 131418864k used, 612692k free, 212104k buffersSwap: 16777212k total,  0k used, 16777212k free, 14648144k cached
 PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND14920 mysql  20 0 125g 109g 6164 S 6.6 87.0 27357:08 mysqld

可以看出mysqld进程占用内存最大,也可以这样查:

[root@localhost ~]# ps -e -o 'pid,comm,args,pcpu,rsz,vsz,stime,user,uid' | grep -E 'PID|mysql' |grep -v grep PID COMMAND   COMMAND      %CPU RSZ VSZ STIME USER  UID25339 mysqld   /export/servers/mysql/bin/m 9.4 115001324 130738976 2017 mysql 50032070 mysqld_safe  /bin/sh /export/servers/mys 0.0 296 106308 2017 root   0

RSZ为进程占用私有内存大小,单位Kb。
VSZ为映射的虚拟内存大小,单位Kb。

通过RSZ/total 也可以算出占用总内存比例。

二、查看给mysql分配的内存

mysql内部主要内存可通过下面语句查出:

MYSQL >SET @giga_bytes = 1024*1024*1024;SELECT (@@key_buffer_size + @@query_cache_size + @@tmp_table_size + @@innodb_buffer_pool_size + @@innodb_additional_mem_pool_size + @@innodb_log_buffer_size + (select count(HOST) from information_schema.processlist)/*@@max_connections*/*(@@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack)) / @giga_bytes AS MAX_MEMORY_GB;

每个参数配置大小:

*************************** 1. row ***************************    @@key_buffer_size: 67108864    @@query_cache_size: 0     @@tmp_table_size: 268435456  @@innodb_buffer_pool_size: 38654705664@@innodb_additional_mem_pool_size: 134217728   @@innodb_log_buffer_size: 8388608    @@max_connections: 3000    @@read_buffer_size: 4194304   @@read_rnd_buffer_size: 4194304    @@sort_buffer_size: 2097152    @@join_buffer_size: 2097152    @@binlog_cache_size: 32768     @@thread_stack: 262144

每个参数配置说明:

innodb_buffer_pool_size 占用内存最大的参数
innodb_additional_mem_pool_size 额外内存,mysql5.7以后移除
innodb_log_buffer_size 重做日志缓存大小
key_buffer_size 只用于MyISAM引擎,不需要太大
tmp_table_size‍ 临时表缓存大小
query_cache_size 查询缓存,建议关闭
max_connections 最大连接数
read_buffer_size read_rnd_buffer_size sort_buffer_size join_buffer_size binlog_cache_size thread_stack 这些参数都跟线程有关,所占内存为这些参数的和*最大连接数。连接数越多占用内存也就越多,建议不超过512K,binlog_cache_size采用系统默认32K,thread_stack默认256K即可

需要给mysql分配多大内存,直接跟以上参数有关。太大会导致内存不足,太小会影响性能,如何分配合理值,还需根据业务情况来定。但业务场景较多,每个业务配置都不一样,会造成运维成本较高。所以能定制出一套适用于绝大多数场景的配置模板就可以了。

1、如果mysql分配的内存比系统内存大

比如系统内存128G,mysql分配的内存已经大于128G,但是系统本身和其它程序也需要内存,甚至mysqldump同样需要内存,所以这样就很容易造成系统内存不足,从而导致OOM。这时我们要查出哪些参数设置比较大,适当降低内存分配。

innodb_buffer_pool在mysql中占有最大内存,将innodb_buffer_pool_size调小可以有效降低OOM问题。但如果设置太小会导致内存刷脏页频率增加,IO增多,从而降低性能。通常我们认为innodb_buffer_pool_size为系统内存的60%~75%最优。

查看buffer_pool的使用情况:

MYSQL >select POOL_ID,POOL_SIZE,FREE_BUFFERS,DATABASE_PAGES,OLD_DATABASE_PAGES,MODIFIED_DATABASE_PAGES,PAGES_MADE_YOUNG,PAGES_NOT_MADE_YOUNG from information_schema.INNODB_BUFFER_POOL_STATS;+---------+-----------+--------------+----------------+--------------------+-------------------------+------------------+----------------------+| POOL_ID | POOL_SIZE | FREE_BUFFERS | DATABASE_PAGES | OLD_DATABASE_PAGES | MODIFIED_DATABASE_PAGES | PAGES_MADE_YOUNG | PAGES_NOT_MADE_YOUNG |+---------+-----------+--------------+----------------+--------------------+-------------------------+------------------+----------------------+|  0 | 611669 |   1024 |   610644 |    225393 |      0 |   309881 |     0 ||  1 | 611669 |   1024 |   610645 |    225393 |      0 |   309816 |     0 ||  2 | 611669 |   1024 |   610645 |    225393 |      0 |   309756 |     0 |+---------+-----------+--------------+----------------+--------------------+-------------------------+------------------+----------------------+

可以看出buffer_pool分了3个实例,POOL_SIZE为每个实例大小,这里为页个数,我们知道mysql页的默认大小为16K,所以单个实例的真正大小为611669*16K,5.6以后要求FREE_BUFFERS至少保留1024个页,少于1024个页时会强制刷脏数据,后面的值可以看出脏页情况。另外如果PAGES_MADE_YOUNG远大于PAGES_NOT_MADE_YOUNG页数,那么此时内存使用就可能比较大,可以适当降低innodb_buffer_pool_size大小。

另一篇文章也有对buffer_pool的介绍:一条命令解读InnoDB存储引擎—show engine innodb status

如果innodb_buffer_pool_size不是很大,但内存还是很高,也可能是由于并发线程太多导致,需要确认是不是应用异常,还是需要调整max_connections最大连接。如果连接太多,每个连接也会占用独立的内存,read、sort、join缓存都是session级别,连接越多需要内存就越多,所以这些参数也不能设置太大。

需要注意的是一些参数不支持动态修改,只能先修改配置文件然后重启mysql才能生效,所以在mysql启动之前,一定要把参数值确认好。

2、如果mysql分配的内存比系统内存小

如果mysql参数设置都比较合理,但是仍然出现oom,那么可能是由于mysql在系统层面所需内存不足导致,因为mysql读取表时,如果同时有多个session引用一个表则会创建多个表对象,这样虽然减少了内部表锁的争用,但是会加大内存使用量。

首先,可以通过lsof -p pid查看进程打开的系统文件数,pid为mysqld的进程号。

[root@localhost ~]# ps -ef | grep mysqld[root@localhost ~]# lsof -p 3455COMMAND PID USER FD TYPE DEVICE SIZE/OFF  NODE NAMEmysqld 30012 mysql cwd DIR  8,3  12288 58982404 /mysql/datamysqld 30012 mysql mem REG  8,1  599392 272082 /lib64/libm-2.12.somysqld 30012 mysql mem REG  8,1  91096 272089 /lib64/libz.so.1.2.3mysqld 30012 mysql mem REG  8,1  93320 272083 /lib64/libgcc_s-4.4.7-20120601.so.1mysqld 30012 mysql mem REG  8,1  43392 272095 /lib64/libcrypt-2.12.somysqld 30012 mysql 10uW REG  8,3 536870912 59015176 /mysql/data/ib_logfile0mysqld 30012 mysql 11uW REG  8,3 536870912 59015177 /mysql/data/ib_logfile1mysqld 30012 mysql 12uW REG  8,3 536870912 59015178 /mysql/data/ib_logfile2mysqld 30012 mysql 13uW REG  8,3 675282944 59001816 /mysql/data/test/table6.ibdmysqld 30012 mysql 14uW REG  8,3 2155872256 58985613 /mysql/data/test/table487.ibdmysqld 30012 mysql 15u REG  8,3   0 58982414 /mysql/tmp/ibhNDzPM (deleted)mysqld 30012 mysql 16uW REG  8,3 2306867200 58983861 /mysql/data/test/table327.ibdmysqld 30012 mysql 17uW REG  8,3 4169138176 58985467 /mysql/data/test/table615.ibdmysqld 30012 mysql 18uW REG  8,3 79691776 59020641 /mysql/data/test/table_v199_20170920.ibdmysqld 30012 mysql 19uW REG  8,3 67108864 59015043 /mysql/data/test/table_v39_20170920.ibdmysqld 30012 mysql 20uW REG  8,3 75497472 59014992 /mysql/data/test/table_v7_20170920.ibdmysqld 30012 mysql 21uW REG  8,3 83886080 59019735 /mysql/data/test/table_v167_20170920.ibdmysqld 30012 mysql 22uW REG  8,3 1367343104 58997684 /mysql/data/popfin6/table_uuid6.ibdmysqld 30012 mysql 23uW REG  8,3 1275068416 58984491 /mysql/data/test/table_uuid7.ibd...[root@localhost ~]# lsof -p 3455 |grep ibd|wc -l54869

查看mysql服务打开文件数限制:

MySQL >show global variables like 'open_files_limit';+------------------+-------+| Variable_name | Value |+------------------+-------+| open_files_limit | 65535 |+------------------+-------+

查看操作系统打开文件数限制:

[root@localhost ~]# ulimit -amax memory size   (kbytes, -m) unlimitedopen files      (-n) 65535

如果此时打开的文件很多,内存也会占用很多。

其次,还需看一下table_open_cache,当打开一个表后会把这个表的文件描述符缓存下来。

MYSQL >show global variables like 'table_open_cache';+------------------+-------+| Variable_name | Value |+------------------+-------+| table_open_cache | 16384 |+------------------+-------+MYSQL >show global status like '%open%tables%';+------------------------+--------+| Variable_name   | Value |+------------------------+--------+| Open_tables   | 16384 || Opened_tables   | 401374 |+------------------------+--------+

通过以上两个值来判断 table_open_cache 是否到达瓶颈。
当缓存中的值open_tables 临近到了 table_open_cache 值的时候,说明表缓存池快要满了,但Opened_tables 还在一直有新的增长,这说明还有很多未被缓存的表。

用show open tables from schema命令,可以查看table_open_cache中缓存的表,重复打开的表仅显示一个

MYSQL >show open tables from sysbenchtest;+--------------+----------+--------+-------------+| Database  | Table | In_use | Name_locked |+--------------+----------+--------+-------------+| sysbenchtest | sbtest1 |  1 |   0 || sysbenchtest | sbtest2 |  0 |   0 || sysbenchtest | sbtest3 |  0 |   0 || sysbenchtest | sbtest4 |  0 |   0 || sysbenchtest | sbtest5 |  0 |   0 |

In_use显示当前正在使用此表的线程数,如果大于0也意味着此表被锁。

Name_locked只适用于DROP和RENAME,在执行DROP或RENAME时,table_open_cache中的表文件描述符会被移除,所以不会看到除0以外的其他值。

一般在库表比较多的情况下(分库分表)很容易出现内存占用较大的情况。如果要解决根源,还是需要对库表进行拆分。

3、MYSQL内部其他内存

information_schema下的表都使用的都是MEMORY存储引擎,数据只在内存中保留,启动时加载,关闭后释放。

查看除系统库外是否有MEMORY引擎表:

MySQL >select * from information_schema.tables where engine='MEMORY' and TABLE_SCHEMA !='information_schema';

如果业务有使用MEMORY存储引擎的,尽量改成innodb引擎。

4、MYSQL事件内存指标

从MySQL5.7开始,在performance_schema中会记录内存分配。

查看哪些指标启动了内存收集功能:

MySQL >select * from performance_schema.setup_instruments where NAME LIKE 'memory/%';

启动需要收集内存的指标:

MySQL >UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%';

指标的内存收集结果会汇总到到sys库下的视图中:

MySQL root@[sys]>show tables like 'memory%';+-----------------------------------+| Tables_in_sys (memory%)   |+-----------------------------------+| memory_by_host_by_current_bytes || memory_by_thread_by_current_bytes || memory_by_user_by_current_bytes || memory_global_by_current_bytes || memory_global_total    |+-----------------------------------+

这些视图总结了内存使用情况,按事件类型分组,默认降序排列:

MySQL >select event_name,current_count,current_alloc,high_alloc from sys.memory_global_by_current_bytes where current_count > 0;+--------------------------------------------------------------------------------+---------------+---------------+-------------+| event_name                  | current_count | current_alloc | high_alloc |+--------------------------------------------------------------------------------+---------------+---------------+-------------+| memory/performance_schema/table_handles          |   10 | 90.62 MiB  | 90.62 MiB || memory/performance_schema/events_statements_summary_by_thread_by_event_name |    3 | 26.01 MiB  | 26.01 MiB || memory/performance_schema/memory_summary_by_thread_by_event_name    |    3 | 16.88 MiB  | 16.88 MiB || memory/performance_schema/events_statements_history_long      |    1 | 13.66 MiB  | 13.66 MiB || memory/performance_schema/events_statements_history       |    3 | 10.49 MiB  | 10.49 MiB || memory/performance_schema/events_statements_current       |    3 | 10.49 MiB  | 10.49 MiB |...

总结:

通过以上排查能大体知道哪些占用内存较多,针对内存占用较多的地方再做具体优化。正像文章开头所说的,内存溢出已经是软件开发历史上存在了近40年的“老大难”问题,更何况数据库环境更加复杂,SQL语法、数据类型、数据大小等这些因素都与内存有关,所以在设计使用上更要多想内存溢出问题。

以上就是MySQL OOM(内存溢出)的解决思路的详细内容,更多关于MySQL OOM(内存溢出)的解决的资料请关注我们其它相关文章!

(0)

相关推荐

  • MySQL OOM 系列三 摆脱MySQL被Kill的厄运

    前面两章,我们分析了Linux内存分配的策略以及Linux通过使用 OOM_Killer的机制解决了"超售"引起的风险,MySQL同其他的应用程序一样,在操作系统允许的范围内也是可以超售的,一般人理解,Innodb_buffer_pool必须小于实际物理内存,否则MySQL会启动失败.其实这是一个误区,这个不是MySQL层控制的,这个是操作系统(OS)层控制的,就是前面提到的/proc/sys/overcommit_memory控制OS是否允许"超售".如果允许&q

  • MySQL OOM 系统二 OOM Killer

    这里就涉及到一个问题,到底Kill掉谁呢?一般稍微了解一些Linux内核的同学第一反应是谁用的最多,就Kill掉谁.这当然是Linux内核首先考虑的一种重要因素,但是也不完全是这样的,我们查一些Linux的内核方面的资料,可以知道其实Kill谁是由/proc/<pid>/oom_score来决定的,这个值每个进程一个,是由Linux内核的oom_badness()函数负责计算的.那下面我们来仔细读一读badness()函数. 在badness()函数的注释部分,写明了badness()函数的处

  • MySQL Slave 触发 oom-killer解决方法

    最近经常有收到MySQL实例类似内存不足的报警信息,登陆到服务器上一看发现MySQL 吃掉了99%的内存,God ! 有时候没有及时处理,内核就会自己帮我们重启下MySQL,然后我们就可以看到 dmesg 信息有如下记录: Mar 9 11:29:16 xxxxxx kernel: mysqld invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0 Mar 9 11:29:16 xxxxxx kerne

  • MySQL OOM 系列一 Linux内存分配

    RDS(网易云关系数据库服务)上线已经有一段时间,陆续不断有产品迁入到了RDS中,在线上运维的过程中,也遇到了一些曾经没有考虑到,或者考虑的不全的东西.后续有时间可以分享给大家. 今天想提到的是线上一个4G的RDS实例,发生了OOM(out of memory)的问题,MySQL进程被直接Kill掉了.在解释这个问题的时候,我们首先需要从Linux系统内存分配策略讲起.     一般写C语言程序,我们习惯使用malloc动态的申请内存空间(Java由JVM负责内存管理),malloc函数会向操作

  • 关于MySQL的整型数据的内存溢出问题的应对方法

    今天接到一个朋友电话说是觉的数据库被别人更改了,出现数据不对的问题 .经过很久的排查是数据类型溢出了(发生问题的版本是MySQL 5.1).后来通过给朋友那边把MySQL 5.1升级到MySQL 5.5去解决这个问题. 这也让我有兴趣去了解一下MySQL不同版本数据类型溢出的处理机制. 先看一下MySQL支持的整型数及大小,存储空间: 另外请记着mysql的数据处理会转成bigint处理,所以这里就用bigint几个测试: SELECTCAST(0ASUNSIGNED)-1; SELECT922

  • PHP内存溢出的解决方法详解

    目录 1.处理数组时出现内存溢出 2.使用sql查询数据,查出来很多,导致内存溢出 3.假定日志中存放的记录数为500000条,那么解决方案如下 4.上传excel文件时,出现内存溢出的情况 什么是内存溢出 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存. 引起内存溢出的原因有很多种,常见的有以下几种: 1 内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 2 集合类中有对对象的引用,使用完后未清空: 3 代码中存在死循环

  • Java基础之堆内存溢出的解决

    一.实战-内存溢出 堆内存溢出 栈内存溢出 方法区溢出 直接内存溢出 二.实战-堆内存溢出 演示堆内存溢出代码,并且定位问题 总结堆内存溢出的场景与解决方案 分析商城项目中可能存在堆内存溢出的代码并且解决 三.堆内存溢出演示代码 public class HeapOOMTest { private List<String> oomList = new ArrayList<>(); public static void main(String[] args) { HeapOOMTes

  • ASP在ACCESS中模糊查询"内存溢出"的解决方法

    今天在日常维护一个网站时,发现该网站的留言程序没有经过严格的验证过滤,导致了将近十万条垃圾数据.而其中又不乏重要信息,需要清理数据,以及增加更为严格的验证措施. 而通过在数据库中直接删除又不太科学,会误删很多重要信息. 通过 模糊查询语句: 复制代码 代码如下: select * from Feedback where Comments like '%http%' 结果:"内存溢出" 经过不断的搜索,找出了问题的主要原因: asp 中用 LIKE 关键字查询日文符号就会出错,比如说Ch

  • 基于Java内存溢出的解决方法详解

    一.内存溢出类型1.java.lang.OutOfMemoryError: PermGen spaceJVM管理两种类型的内存,堆和非堆.堆是给开发人员用的上面说的就是,是在JVM启动时创建:非堆是留给JVM自己用的,用来存放类的信息的.它和堆不同,运行期内GC不会释放空间.如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改

  • ASP在ACCESS中模糊查询"内存溢出"的解决方法

    今天在日常维护一个网站时,发现该网站的留言程序没有经过严格的验证过滤,导致了将近十万条垃圾数据.而其中又不乏重要信息,需要清理数据,以及增加更为严格的验证措施. 而通过在数据库中直接删除又不太科学,会误删很多重要信息. 通过 模糊查询语句: 复制代码 代码如下: select * from Feedback where Comments like '%http%' 结果:"内存溢出" 经过不断的搜索,找出了问题的主要原因: asp 中用 LIKE 关键字查询日文符号就会出错,比如说Ch

  • jvm内存溢出解决方法(jvm内存溢出怎么解决)

    java.lang.OutOfMemoryError: PermGen space 发现很多人把问题归因于: spring,hibernate,tomcat,因为他们动态产生类,导致JVM中的permanent heap溢出 .然后解决方法众说纷纭,有人说升级 tomcat版本到最新甚至干脆不用tomcat.还有人怀疑spring的问题,在spring论坛上讨论很激烈,因为spring在AOP时使用CBLIB会动态产生很多类. 但问题是为什么这些王牌的开源会出现同一个问题呢,那么是不是更基础的原

  • 一个JSP页面导致的tomcat内存溢出的解决方法

    今天新能测试组的同事找我看一个奇怪的现象.一个tomcat应用,里面只有一个单纯的jsp页面,而且这个jsp页面没有任何java代码(想用这个jsp页面测试在她的服务器上的一个tomcat的最大QPS).但是用loadrunner压测了几分钟之后,分配了1024M堆内存的tomcat居然包heap space outofmemory!这个页面的代码如下: 复制代码 代码如下: <%@ page language="java" contentType="text/html

  • Java内存各部分OOM出现原因及解决方法(必看)

    一,jvm内存区域 1,程序计数器 一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. 2,java栈 与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型,对象引用(一个指向对象起始地址的引用指针或一个代表对象的句柄),reeturnAddress类型(指向一条字节码指令的地址) 栈区域有两种异常类型:如果线程请求的栈深度大于虚拟机所允许的深度,将抛StrackOverflowError异常:如果虚拟机栈可以动态扩展(大部分虚拟机都可动

  • Java虚拟机常见内存溢出错误汇总

    一.引言 从事java开发的小伙伴在平时的开发工作中,应该会遇见各式各样的异常和错误,在实际工作中积累的异常或者错误越多,趟过的坑越多,就会使我们编码更加的健壮,就会本能地避开很多严重的坑.以下介绍几个Java虚拟机常见内存溢出错误.以此警示,避免生产血案. 二.模拟Java虚拟机常见内存溢出错误 1.内存溢出之栈溢出错误 package com.jayway.oom; /** * 栈溢出错误 * 虚拟机参数:-Xms10m -Xmx10m * 抛出异常:Exception in thread

  • TOMCAT内存溢出及大小调整的实现方法

    一.tomcat内存设置问题 收藏 在使用Java程序从数据库中查询大量的数据或是应用服务器(如tomcat.jboss,weblogic)加载jar包时会出现java.lang.OutOfMemoryError异常.这主要是由于应用服务器的内存不足引起的.这种异常常有以下几种情况(以下以tomcat环境为例,其它WEB服务器如jboss,weblogic等是同一个道理): 1. java.lang.OutOfMemoryError: PermGen space PermGen space的全称

随机推荐