linux内核3.6版本及以下的bug引发的故障--cpu使用率100%

现象: 
        旗舰店运价库cpu使用率100%,load升高,导致后续的请求失败。 
        重启服务器,cpu、load恢复正常。

触发条件: 
       (1)linux内核3.6版本及以下。 (线上机器大部分是2.6.32)
       (2)mysql-connector-java5.1.31版本及以下。(各业务线需要自己check)
       (3)mysql-client没有设置socketTimeout。 (各业务线需要自己check)
       (4)杀死mysql-server与mysql-client的连接处于mysql-server端的线程。 (dba经常会杀死慢查询)
        当(1)(2)(3)同时具备,只要触发(4),客户端连接线程就会死循环。杀死一个mysql-server端线程,客户端就会死循环一个,占用一个cpu内核。

具体原因:

    Linux kernels version 3.6 and earlier (including 2.6.32) have a bug [1] which makes requests for the amount of available bytes to read in a socket in CLOSE_WAIT state to return 1 even after the EOF has been read.

    This bug makes SocketInputStream.available return 1 for sockets in CLOSE_WAIT state and causes a seemly infinite loop in MysqlIO.clearInputStream where it attempts to read from the socket until the number of available bytes reaches 0, but there is nothing to read.

出处:  https://bugs.mysql.com/bug.php?id=73053

mysql官网5.1.32版本的变更记录,有提到这个bug:

A bug in the Linux kernel version 3.6 and earlier caused the MysqlIO.clearInputStream() method to enter an endless loop. This fix changes the way the looping condition is evaluated, in order to avoid the problem. (Bug #19022745, Bug #73053)

出处:https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/news-5-1-32.html

mysql高版本(已兼容linux低版本内核的bug),附截图:

解决方案: 
      (1)升级mysql-connector-java版本到5.1.32及以上。 或者
      (2)升级linux内核版本到3.7及以上。                         或者
      (3)客户端设置socket读超时时间。(杀死server端线程,客户端线程立马释放,没有到达超时时间。为什么能生效,不是很清楚,有知道的可以回复邮件) 
       推荐使用(1)

当时分析问题的线程栈信息如下: 
      at java.net.PlainSocketImpl.socketAvailable(Native Method)
      at java.net.AbstractPlainSocketImpl.available(
      AbstractPlainSocketImpl.java:478)
      - locked <0x000000070ed04a40> (a java.net.SocksSocketImpl)
      at java.net.SocketInputStream.available(SocketInputStream.java:245)
      at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:72)
      at com.mysql.jdbc.util.ReadAheadInputStream.skip(ReadAheadInputStream.java:300)
      at com.mysql.jdbc.MysqlIO.clearInputStream(MysqlIO.java:948)
      at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2404)
      at com.mysql.jdbc.ConnectionImpl.pingInternal(Unknown Source)
      at com.mysql.jdbc.ConnectionImpl.execSQL(Unknown Source)
      - locked <0x000000070ed04c10> (a com.mysql.jdbc.JDBC4Connection)  
     at com.mysql.jdbc.ConnectionImpl.execSQL(Unknown Source)
     at com.mysql.jdbc.StatementImpl.execute(Unknown Source)
     - locked <0x000000070ed04c10> (a com.mysql.jdbc.JDBC4Connection)
     at com.mysql.jdbc.StatementImpl.execute(Unknown Source)

查问题的过程:
       1. 重启机器,cpu使用率恢复正常。 但是保留一台机器做现场。
       2. 使用jstack命令与top命令分析现场机器,发现执行时间长和cpu使用率高的线程,死循环在MysqlIO.clearInputStream()。
       3. 在网上查阅资料,发现是linux内核3.6版本及以下的bug导致。 但是不知道怎么触发。
       4. 询问dba,当时dba在杀死mysql-server端的慢查询,时间是吻合的。 猜测触发条件之一是,杀死mysql-server与mysql-client的连接 处于mysql-server端的线程。

复现过程:
       1. 使用内核版本2.6.32的linux部署应用服务,应用服务使用的mysql-connector-java版本为5.1.21, mysql-client没有设置socketTimeout。 (与线上环境一致)
       2. 更改应用服务里的sql语句,其实是增加了休眠,方便能在mysql-server端查看此线程。 sql语句由select... 变为 select sleep(10) ...
       3. 调用http接口,触发测试的查询语句。
       4. 登录mysql-server,使用命令 show processlist; 查看测试的查询语句,得到线程id。 kill + 线程id, 杀死正在执行的线程。
       5. 应用服务器,cpu其中1核使用率达到100%, 并且http接口,一直没有响应返回(应用服务器线程死循环了)。
       6. 重复步骤(3)、(4),每杀死mysql-server端的一个线程,cpu的一个核使用率就会达到100%。

注意:如果应用服务器 打过补丁tcp: fix FIONREAD/SIOCINQ, 无法复现。

解决方案确定的过程:
       1. 升级mysql-connector-java版本到5.1.34,其他条件不变。 杀死mysql-server端的线程,应用服务器cpu没有变化,http接口响应立马返回。
       2. 升级linux内核版本到3.18.48,其他条件不变。 杀死mysql-server端的线程,应用服务器cpu没有变化,http接口响应立马返回。
       3. 设置mysql-client端超时时间2分钟,其他条件不变。 杀死mysql-server端的线程,应用服务器cpu没有变化,http接口响应立马返回。没有等到2分钟超时,http接口响应才返回。

时间: 2024-08-27 00:11:41

linux内核3.6版本及以下的bug引发的故障--cpu使用率100%的相关文章

LinuxConsole 2.5 发布,采用Linux内核4.1版本

LinuxConsole 2.5 发布,一个轻量级的.独立的分布特征和伴侣的版本LXDE桌面环境.据菜鸟教程QKXue.NET了解该版本采用了Linux内核4.1版本,提供了用于引导对UEFI的支持功能的硬件,包括许多游戏. 更多介绍内容请查看软件主页:LinuxConsole 首页 LinuxConsole是一份GNU/Linux操作系统,用户只需要进行最少的配置就能让它工作.

Linux 内核、系统 版本信息 获取

1.查看内核 ## 通过读取 /proc/version 文件,获取内核版本信息 # cat /proc/version ## 直接通过 uname命令 获取内核信息 # uname -r   2.查看系统版本 ## lsb_release 命令查看 ## lsb_release -a 查看系统所有信息 ## lsb_release -sc 只查看系统版本发行号 # lsb_release -a # lsb_release -sc ## 读取 /etc/issue 文件,获取系统版本信息,适用于

Linux内核4.4版本带来的网络新特性

本文题目有点大,但其实我只想描述一些我个人一直比较关注的特性,并且不会太详细,跟往常一样,主要是帮忙理清思路的,不会分析源码.这主要是为了哪一天突然忘了的时候,一目十行扫一眼就能记忆当时的理解,不然写的太细节了,自己都看不懂了. Lockless TCP listener 先 从TCP的syncookie说起,如果都能使用syncookie机制该有多好,但是不能,因为它会丢失很多选项协商信息,这些信息对TCP的性能至关 重要.TCP的syncookie主要是为了防止半连接的syn flood攻击

Linux内核版本类型

对于Linux内核发布的版本类型有如下,也是自己的理解: [mainline]:主线版本,由Linux Torvalds维护和发布. [stable/EOL]:稳定版本,每个由主线发布的版本都叫做稳定版本,并且不会有新的特性加入,同时也可以向主线版本提交bug修复和补丁,支持到下一个稳定版本发布:但是,较老的版本不再提供维护将标记为EOL. [longterm]:长期维护版本,从稳定版本分出来进行长期维护,原理和稳定版本一样:有些长期版本能提供十几年之久,同时如果较老的版本不再维护也会标记为EO

获取Android源码跟官方Linux内核源码

最近在探索安卓源码跟ROM,有幸买了本书跟在网上看到一些交教程,先做一些笔记,以备不时之需... 1.了解git和repo 2.环境配置 首先最先的肯定是JDK和SDK的安装,网上教程很多,也可以参考Google官方提供的: https://source.android.com/source/initializing.html (a)安装git 和curl: apt-get install git-core curl (b)安装repo: 创建存放repo目录 # mkdir ~/bin  # 

初识Linux内核

Linux诞生于1991年,出自LinusTorvalds. POSIX 表示可移植操作系统接口(Portable Operating System Interface).POSIX是在Uni标准化过程中出现的产物.POSIX 1003.1标准定义了一个最小的Unix操作系统接口任何操作系统只有符合这一标准,才有可能运行Unix程序. GNU 是 GNU Is Not Unix 的递归缩写,是自由软件基金会的一个项目.GNU 项目产品包括 emacs 编辑器.著名的GNU C 和 Gcc编译器等

nginx 高并发参数配置及linux内核参数优化

一.一般来说nginx 配置文件中对优化比较有作用的为以下几项: 1.  worker_processes 8; nginx 进程数,建议按照cpu 数目来指定,一般为它的倍数 (如,2个四核的cpu计为8). 2.  worker_cpu_affinity 00000001 0000001000000100 00001000 00010000 00100000 01000000 10000000; 为每个进程分配cpu,上例中将8 个进程分配到8 个cpu,当然可以写多个,或者将一个进程分配到

学习重新编译Linux内核

一.实验目的学习重新编译Linux内核,理解.掌握Linux内核和发行版本的区别. 二.实验内容在Linux操作系统环境下重新编译内核.实验主要内容:A. 查找并且下载一份内核源代码,本实验使用最新的Linux内核2.6.36.B. 配置内核.C. 编译内核和模块.D. 配置启动文件.本次实验环境是Linux2.6.35内核的环境下,下载并重新编译内核源代码(2.6.36):然后,配置GNU的启动引导工具grub,成功运行编译成功的内核. 三.主要仪器设备(必填)Linux环境:utuntu10

Linux内核的整体架构简介

1. 前言 本文是"Linux内核分析"系列文章的第一篇,会以内核的核心功能为出发点,描述Linux内核的整体架构,以及架构之下主要的软件子系统.之后,会介绍Linux内核源文件的目录结构,并和各个软件子系统对应. 注:本文和其它的"Linux内核分析"文章都基于如下约定:  a) 内核版本为Linux 3.10.29(该版本是一个long term的版本,会被Linux社区持续维护至少2年),可以从下面的链接获取:https://www.kernel.org/pu