问题:

进程启动后,线程数迅速上升至最小线程数后,缓慢上升(线程池限制)到数千,然后由于线程过多,CPU飙升到90%。

对外表现为Api无响应或连接超时。

背景

有些数据存在于另一个机房,通过内网专线连接。一个服务程序有4个数据库,其中3个在本地机房,1个在外地。

各种排查,没有解决。

最终的处理方法

Dump进程

  1. 使用进程管理器,创建进程Dump文件。
  2. 使用VisualStudio打开该Dump文件并进行托管调试
  3. 查看并行堆栈,发现大部分线程均处于MySql.Data.MySqlClient.MySqlPoolManager.GetPool这个函数的调用中。并在此处进入了本机代码。处于其他调用堆栈的线程屈指可数。

代码分析

  1. 由于Mysql.Data.dll没有对应的pdb文件(Oracle没有提供),所以在Visual Studio中不能进入其中的代码,因此直接反编译,找到该函数,代码如下:

函数中,第一句的GetKey函数如下,其中有一个lock。其中代码仅仅是赋值,或是在集成认证的情况下才执行。所以卡住的可能性不大。

第二句是个赋值,且MysqlPoolManager.pools是个字段(field),理论上不会卡住。

第二个lock中,如果指定key对应的缓存已存在,则lock会很快返回。如果不存在,则执行new MysqlPool(setttings);函数代码如下:

其主要功能有

  1. 创建一个事件,用于获取连接时的异步等待
  2. 根据settings持久化设置
  3. 初始化池驱动列表、队列
  4. 按照配置的minSize创建指定数量的连接。
  5. 创建一个过程缓存,代码如下

这5个步骤中,最可能耗时较久的是步骤d。其他步骤理论上不会有问题。

步骤d中的代码,虽然就一个函数,但是代码很多。

经过不停的查看代码,发现其主要功能是根据连接字符串中的设置,创建一个指定类型的连接。其底层创建代码如下:

可以看到,任何创建Stream失败的情况都会抛出异常,最终导致连接池创建失败。

其中第一句,GetStream的底层代码如下:

开始连接(BeginConnect)后,即开始了等待。等待的超时默认值如下:

2147483s,即596h。如果有连接到数据库服务器的网络有问题或其他原因导致连接不成功,而也未触发其他导致失败的情况,则会一直等下去。如果推断正确,那么所有线程中,一定有线程的调用堆栈在如下位置:

对Dump文件中的所有线程堆栈排序,有且仅有一个线程处于该调用堆栈处。高亮行正是上述堆栈的函数名CreateSocketStream上面一行就是WaiteOne。之后进入本机代码。

那么根本原因也就清楚了:一个连接的创建卡住了数据库连接创建,间接卡住了连接池的锁,又间接卡住了其他连接池的使用和创建。导致所有数据库连接不可用。所以,所有进入的请求经过运行,全部堆在GetPool这里。

解决方法:

  1. 保证网络正常(跨机房专线稳定性不可控,有人摇晃光纤玩 o(∩_∩)o 或者其他原因导致流量堵塞)
  2. 容易卡的数据库连接分离出去到单独的进程。这样由于不共享锁,所以不会卡住其他线程池的使用和创建。
  3. 需要跨机房的业务,在数据所在机房单独提供api,内网失效时可以走外网。
  4. 容易卡住的线程池连接字符串中设置minPoolSize=0。这样创建连接池时,不预创建连接而影响其他连接池。但是,对于突发流量增长的情况,响应可能不够及时。
  5. 设置一个合理的ConnectionTimeout。可以有效避免连接创建时卡住,导致api无响应和其他副作用。

其他在源代码中发现的需要注意的地方

  1. 连接池中空闲连接的空闲时间是180s。
  2. 清理周期第一次是188s,之后保持180s。
  3. 如果连接池中的空闲连接数大于设置的minPoolSize,则清理空闲连接直到minPoolSize。
  4. ConnectionTimeout用于几个地方
    1. ) 连接socket时的等待超时
    2. ) 连接之后,连接上的读写超时。
    3. ) 从已空且总数达上限的连接池中,等待可用连接时的等待超时

以上所有信息基于.Net版本Mysql.Data 6.9.9版本反编译分析。

一次Mysql连接池卡死导致服务无响应问题分析(.Net Mysql.Data 6.9.9)的更多相关文章

  1. tomcat服务无响应堆栈分析

    tomcat服务突然无响应了,导出内存堆栈和线程堆栈,分析后发现是同步锁使用不合理导致的. [root@prd-dtb-web-01 ~]# [root@prd-dtb-web-01 ~]# jmap ...

  2. ArcGIS Server浏览地图服务无响应原因分析说明

    1.问题描述 从4月17号下午5时起,至18号晚9点,客户单位部分通过ArcGIS Server发布的地图服务(该部分地图服务的数据源为数据库SJZX)无法加载浏览,表现为长时间无响应.同时,通过Ar ...

  3. mysql连接池模块

    如果不想程序在查询数据时卡死或等待过长时间,一般不推荐在node中开启一个连接后全部查询都用这个链接并且不关闭.因为node里面的mysql不像php里的那样会在完成查询后断开,只要不主动断开,连接一 ...

  4. 连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛😿 问题现象: 隔几周就会出现 A服务调用B服务超时 脚趾头想就是防火墙的问题,A、B两服务之间有防火墙 找运维查看防火墙日志确实断掉了tcp连接,但是是因为B服务5分钟没有回包,下面这个表情就是我当时的心情——其实我们在防火墙、A服务、B服务都抓包了,几十个G的t

    连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛

  5. Java Mysql连接池配置和案例分析--超时异常和处理

    前言: 最近在开发服务的时候, 发现服务只要一段时间不用, 下次首次访问总是失败. 该问题影响虽不大, 但终究影响用户体验. 观察日志后发现, mysql连接因长时间空闲而被关闭, 使用时没有死链检测 ...

  6. .Net中如何使用MySql连接池

    提供一份官方的译文.翻译也挺辛苦的!! 6.4 Using Connector/Net with Connection Pooling 6.4在Connector/Net中使用连接池 The Conn ...

  7. workerman如何写mysql连接池

    首先要了解为什么用连接池,连接池能为你解决什么问题 连接池主要的作用1.减少与数据服务器建立TCP连接三次握手及连接关闭四次挥手的开销,从而降低客户端和mysql服务端的负载,缩短请求响应时间2.减少 ...

  8. 解决Mysql连接池被关闭 ,hibernate尝试连接不能连接的问题。 (默认mysql连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池。系统发布第二天访问链接关闭问题。

    解决Mysql连接池被关闭  ,hibernate尝试连接不能连接的问题. (默认MySQL连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池. 所以系统发布第二天访问会 ...

  9. nodejs + redis/mysql 连接池问题

    nodejs + redis/mysql 连接池问题 需不需要连接池 连接池的作用主要是较少每次临时建立连接所带来的开销.初步一看,nodejs运行单线程上,它不能同时使用多个连接,乍一看是不需要连接 ...

随机推荐

  1. 2017 ACM/ICPC Asia Regional Shenyang Online array array array

    2017-09-15 21:05:41 writer:pprp 给出一个序列问能否去掉k的数之后使得整个序列不是递增也不是递减的 先求出LIS,然后倒序求出最长递减子序列长度,然后判断去k的数后长度是 ...

  2. HTTP-API-DESIGN 怎样设计一个合理的 HTTP API (一)

    这个附件的幻灯片是我最近给团队分享关于设计 HTTP API 的时候,结合 这篇 和我们团队历史上的一些错误,总结出来一些适合内部的经验. 简介. 这次分享主要关注以下几部分: HTTP + JSON ...

  3. nginx+nagios使用用户名密码鉴权设置

    1.使用htpasswd生成密码 使用apache生成/usr/local/apache2/bin/htpasswd -c ./htpasswd.users nagiosadmin 拷贝到nginx的 ...

  4. 何时使用img标签,何时使用background-image背景图像

    在什么情况下更适合使用HTML IMG标签来显示一个图像,而不是一个CSS有背景图像,反之亦然? 如下场景使用img标签比较合适: 1.如果图像是等内容的一部分或图表或人(真正的人,而不是股票图人), ...

  5. Ansible 小手册系列 九(Playbook)

    playbook是由一个或多个"play"组成的列表.play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色.从根本上来讲所谓task无非 ...

  6. python:使用itchat实现手机控制电脑

    1.准备材料 首先电脑上需要安装了python,安装了opencv更好(非必需) 如果安装了opencv的话,在opencv的python目录下找到cv2.pyd,将该文件放到python的库搜索路径 ...

  7. laravel中新建文件并保存数据到文件中

    //base_path()获取当前的绝对路径 $path=base_path().'\config\web.php'; $str='abcdefg'; //要声明的字符串 file_put_conte ...

  8. Oracle 使用小技巧

    1.小数转换成字符往往会丢失前面的零. 解决方法: to_char(0.12345,'fm9999999990.00'); 2.除数为零的话oracle老是报错. 解决方法: decode(B,0,0 ...

  9. C# 与vb.net 的Dictionary(字典)的键、值排序

    项目中可能需要用到Dictionary 排序,于是先做了一个小demo ,网上搜索真的没有能满足我需要的,都是类似的,于是理解改造,一上午就在查找,实践中过去了.现在把它实现了,把代码贴出来,算是一个 ...

  10. gitblit-禁用用户

    使用管理员账号(此处演示使用admin)登录到系统 右上角,找到用户菜单 再用户列表,找到要进行权限管理的用户,(演示alex) 禁用用户,取消权限 验证已经禁用 pull 权限被禁止