5834871 K bytes of memory
这一段计算了当前配置下, 需要的最大内存数, 大概换算了一下, 是234G;
这么高, 明显是有问题的, 联想到82%内存使用率的报警信息, 推测是内存耗尽造成的;
用max_used_connections来算一下使用的内存的话,有约1.5G;
加上BP的9.6G, 有11G了, 算上MySQL本身占用的一部分内存, 确实达到了比较高的程度;
但是看了一下kernel和message, 都没有发现系统出现OOM的日志, 应该不是系统kill的;
再看看堆栈相关的信息, 在里面记录了MySQL crash时的状态;
stack_bottom = 7f6141b36e80 thread_stack 0x40000
/usr/sbin/mysqld(my_print_stacktrace+0x2c)[0xe77fec]
/usr/sbin/mysqld(handle_fatal_signal+0x459)[0x7a7019]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xf8d0)[0x7f643257a8d0]
/usr/sbin/mysqld(_ZN16Partition_helper25handle_ordered_index_scanEPh+0x5c)[0xbbabec]
/usr/sbin/mysqld(_ZN7handler13ha_index_lastEPh+0x1b0)[0x7f4410]
/usr/sbin/mysqld(_Z14join_read_lastP7QEP_TAB+0x65)[0xc1f605]
/usr/sbin/mysqld(_Z10sub_selectP4JOINP7QEP_TABb+0x11b)[0xc25e4b]
/usr/sbin/mysqld(_ZN4JOIN4execEv+0x3b8)[0xc1ea78]
/usr/sbin/mysqld(_Z12handle_queryP3THDP3LEXP12Query_resultyy+0x238)[0xc8e408]
/usr/sbin/mysqld[0x770ccf]
/usr/sbin/mysqld(_Z21mysql_execute_commandP3THDb+0x3403)[0xc51103]
/usr/sbin/mysqld(_Z11mysql_parseP3THDP12Parser_state+0x3ad)[0xc531bd]
/usr/sbin/mysqld(_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command+0x817)[0xc53a47]
/usr/sbin/mysqld(_Z10do_commandP3THD+0x18f)[0xc54faf]
/usr/sbin/mysqld(handle_connection+0x278)[0xd108d8]
/usr/sbin/mysqld(pfs_spawn_thread+0x1b4)[0xe90784]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x80a4)[0x7f64325730a4]
/lib/x86_64-linux-gnu/libc.so.6(clone+0x6d)[0x7f6430e1b87d]
从红字等地方的信息, 可以推断出当时MySQL是正在执行查询, 这些查询中有join, 也有subquery, 且查询的表包含了分区表;
可以预料到在crash的时候, MySQL执行这些语句时肯定需要申请一部分join用的buffer, 同时子查询也会建立临时表, 都需要占用内存空间;
同时还有分区表的使用, 看了一下当时候分区表的大小:
发现当天的数据超过了BP的大小, 且用到分区表的查询走的全表扫描, 并且还有order by, 会用到sort的buffer, 且由于全表扫描的数据很多, 这个buffer有可能是需要申请满的;
综合这些信息, 基本确认是内存耗尽造成了MySQL crash;
那么根据堆栈的信息尝试还原crash时的场景:
在内存占用率很高的情况下, MySQL thread在执行较大表的查询时, 无法再申请到足够的内存(sort的buffer, join的buffer等), 因此发生了crash;
处理方式:
最终把BP从9.6G调整为9G, 并把sort, read等buffer的数值调整到了4M, 其他的buffer也调低了;
PS: 算是疏忽吧, 因为说在生产环境已经用这么一套配置很久了, 没出过问题, 所以也没有仔细的排查配置文件中的设置;
PSS: sort的buffer原来是多少 32M...sort的buffer还是per thread的...失职了..._(:з」∠)_
-------------------------------------------------------------------------------------------------后续---------------------------------------------------------------------------------------------------------------
峰回路转.....在调整了buffer的数量以后, 不可能再出现内存不够的现象了, 然后crash的现象重现了;
而且是主库和备库在非常短的时间内都发生了crash;
报错信息除了pointer不同以外, 堆栈的信息也是完全一致;
包括那个语句;
在之前出问题的时候, 记录了一条语句:
select * from test where time>='2016-07-29 00:00:00' and time<='2016-07-29 23:59:59' and tag in (2,3,6) order by id desc limit 2000
在后来重现的时候, 两次crash的语句中, 记录的是同样的语句, (而且堆栈的输出信息也是完全一样) , 仅仅只是时间不一样:
select * from test where time>='2016-08-09 00:00:00' and time<='2016-08-09 23:59:59' and tag in (2,3,6) order by id desc limit 2000
如果是因为内存or系统资源的不可用导致了crash的话, 不可能有这么巧合的事情, 都是这个语句;
so, 在被拉起来的备库上跑了一下这个语句, 结果MySQL马上就crash了...
那么明显就是这个语句的问题了, order by desc + limit, 看上去并没什么问题, 看看explain的结果
虽然好久没做开发了, 但是filtered在100%的情况下, rows只有1还是挺奇怪的, 一整张表只有一行数据, 但是还有这种查询一整天的语句;
看看表的结构;
隐去生产库上的一部分信息, 留下关键的部分;
分区表的分区有问题....
问过业务以后, 原来是这个功能还没做完, 所以表相关的操作并没有一直执行;
但是这个功能的页面没有屏蔽, 所以对应的那条语句是有可能被触发的;
考虑到用那条语句可以必现这个crash, 且输出的堆栈信息和之前完全一致,
所以确定是这个分区表的分区缺失的前提下, 触发那个查询语句的时导致了MySQL的Crash;
处理方式:
虽然最后还是找到了问题所在, 但是最开始的时候还是被buffer和内存使用率的现象误导了, too young......
PS:本来还是觉得分区表在5.7改进了一点以后, 应该还挺好用的.....恩, 现在持保留意见....._(:з