一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例
最近,项目中遇到了数据库连接不够的问题。
异常信息
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
Data source rejected establishment of connection, message from server: "Too many connections"
根据更详细的错误信息,我定位到报错的函数位置。
关键函数
/**
* 判断数据库是否存在
*/
public boolean hasDatabaseByKey(String name) {
boolean containsKey = dataSourceMap.containsKey(name);
if (containsKey) { try {
//根据数据库名称,把BoneCP数据源中的数据库参数取出来,用户名、密码、URL
Object obj = dataSourceMap.get(name);
com.jolbox.bonecp.BoneCPDataSource dataSource = (com.jolbox.bonecp.BoneCPDataSource) obj; String password = dataSource.getPassword();
String username = dataSource.getUsername();
String url = dataSource.getJdbcUrl();
//建立新的数据库连接--这一行代码抛出“Too many connections”异常
Connection con = DriverManager.getConnection(url, username, password);
//关闭连接
if (con != null) {
con.close();
}
return true; } catch (Exception e) {
LOG.error("Database name error:" + name);
e.printStackTrace();
}
}
return false;
}
是否正常关闭了数据库连接
上述代码的主要功能是,根据前端传入的数据库名字,如“test”,检测该数据库的配置是否正确。
最初想到的,上面的数据库连接con可能没有正常关闭。
经过单步跟踪debug,和使用MySQL的 show processlist命令,发现所有的连接确实是正常关闭的。
因此,上述代码的功能是没有问题的。
功能没有问题,但是上述代码还是存在其它问题的
a.数据库连接应该在finally里关闭。
b.这种检测方法,每次都需要打开一个连接,比较耗费时间。
一种好的方法是,在系统初始化的时候,检查所有的数据源配置是否正确,把结果
用Map保存起来,("test",true)表明test数据库配置正确;
或者每一次检查,先从缓存中取,如果存在直接使用,否则,打开连接,进行检查,然后保存结果到缓存中。
问题根源
项目中使用了多个数据库, 数据源公共的配置如下。
<bean id="abstractDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="username" value="root" />
<property name="password" value="xxxx" />
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="maxConnectionsPerPartition" value="50"/>
<property name="minConnectionsPerPartition" value="2"/>
</bean>
最近新增了10个数据源配置。
也就是说,现在新增的数据库连接,最高可以达到50*(10+1)=550个了。(不是50*1=50个)
而数据库MySQL的默认max_connections是100。
因此,我们在访问Web项目,然后频繁切换项目的时候,数据库连接池中的数目,已经达到了100。
这个时候,我们再去手动创建数据库连接,就会失败。
上文代码的更多配置信息
@Resource
private Map<String, Object> dataSourceMap;
<!-- 配置多数据源映射关系 -->
<bean id="dataSourceMap" class="java.util.HashMap">
<constructor-arg>
<map key-type="java.lang.String">
<entry key="demo1">
<bean parent="abstractDataSource" >
<property name="jdbcUrl"
value="jdbc:mysql://ip:3306/demo1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" />
</bean>
</entry> <entry key="demo2">
<bean parent="abstractDataSource" >
<property name="jdbcUrl"
value="jdbc:mysql://ip:3306/demo2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" />
</bean>
</entry>
</map>
</constructor-arg>
</bean>
改进后的代码
private static Map<String, Boolean> databaseStatusMap = new HashMap<String, Boolean>();
/**
* 判断数据库是否存在
*/
public boolean hasDatabaseByKey(String name) {
//数据源中是否含有这个数据库名称
boolean containsKey = dataSourceMap.containsKey(name);
//不存在这个数据名字,直接false
if (!containsKey) {
return false;
} //首先从缓存中拿,不为null,表明已经验证过了
Boolean status=databaseStatusMap.get(name);
if(status != null){
return status;
} //缓存中不存在,第1次验证
boolean databaseConfigSucceed = false;
Connection con = null;
try {
Object obj = dataSourceMap.get(name);
com.jolbox.bonecp.BoneCPDataSource dataSource = (com.jolbox.bonecp.BoneCPDataSource) obj; String password = dataSource.getPassword();
String username = dataSource.getUsername();
String url = dataSource.getJdbcUrl(); //验证数据库连接,把结果存到缓存中
con = DriverManager.getConnection(url, username, password); //这个地方con不可能为null,要么是一个正常的连接,要么抛出异常
databaseConfigSucceed = true;
databaseStatusMap.put(name, true);
/*
if (con != null) {
databaseConfigSucceed = true;
databaseStatusMap.put(name, true);
}
else{
databaseConfigSucceed = false;
databaseStatusMap.put(name, false);
}*/ } catch (Exception e) {
//抛出异常,下次不会去重新检查,可能会存在bug,如果第1次检测的时候,网断了或者MySQL挂了或者数据库连接过多,并不能代表配置不正确
//小概率事件,暂时不考虑
LOG.error("Database name error:" + name);
databaseConfigSucceed=false;
databaseStatusMap.put(name, false);
e.printStackTrace();
} finally {
if (con != null) {
try {
//验证完数据库连接,需要手动关闭
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return databaseConfigSucceed;
}
小概率事件
如果在执行 DriverManager.getConnection(url, username, password);获取数据库连接的时候,发生了异常。
如果恰好是第1次检测的时候,网断了或者MySQL挂了或者数据库连接过多,并不能代表配置不正确。
抛出异常,下次就不会去重新检查,可能会存在bug。
不过,系统第一次访问项目的时候,正好出故障的可能性很低。
可能存在的错误情况:
第一次成功,加入缓存为true,如果今后数据库连接打不开,应该提示“不可以打开”,也会提示“可以打开”。
第一次不成功,加入缓存为false,如果今后数据库能够打开,应该提示“可以打开”,也会提示“不可以打开”。
结论
1.在不使用缓存的情况下,每次都通过建立新连接的方式。
优点:可以准确的判断数据库配置是否正确,是否真正能够连接到数据库,代码的可读性和复杂度比较低。
缺点:性能比较差。
2.使用缓存。
优点:性能比较高。
缺点:在不正常情况下,准确性没有保障。代码的可读性和复杂度比较高。
再次改进
缓存,增加“时间限制”,过一段时间后,就失效。
观点
实现的功能越多,越准确,性能越好,代码可能会越来越复杂。
代码的正确性和性能有的时候是“互相排斥”的。
过多的追求完美,也会带来一些负担。
原文参见: http://FansUnion.cn/articles/2961
一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例的更多相关文章
- 一个Web报表项目的性能分析和优化实践(四):MySQL建立索引,唯一索引和组合索引
先大致介绍下项目的数据库信息. 数据库A:主要存放的通用的表,如User.Project.Report等. 数据库B.C.D:一个项目对应一个数据库,而且这几个项目的表是完全一样的. 数据库表的特点 ...
- 一个Web报表项目的性能分析和优化实践(三) :提高Web应用服务器Tomcat的内存配置,并确认配置正确
摘要 上一篇,一个Web报表项目的性能分析和优化实践(一):小试牛刀,统一显示SQL语句执行时间 ,讲述了项目优化的整体背景,重点讲述了统一显示了Web项目SQL语句的执行时间. 本篇,将重点介绍提高 ...
- 一个Web报表项目的性能分析和优化实践(六):设置MySQL的最大连接数(max_connections)
在上一篇文章中"一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例"提到,项目中新增几个数据库 ...
- 一个Web报表项目的性能分析和优化实践(五):重构有助于性能优化么?
项目从初次开发到现在,已经快3年了.期间,有N个工程师参与过. 需求方面:增加减少,反反复复,无数次:人力方面:增加减少,不稳定:时间方面:功能开发着急上线,Bug开发紧急修复. 因此,代码臃肿,问题 ...
- [置顶] 一个Web报表项目的性能分析和优化实践(一):小试牛刀,统一显示SQL语句执行时间
最近,在开发和优化一个报表型的Web项目,底层是Hibernate和MySQL. 当报表数据量大的时候,一个图表要花4秒以上的时间. 以下是我的分析和体会. 1.我首先需要知道哪些函数执行了多少时间 ...
- 一个Web报表项目的性能分析和优化实践(一):小试牛刀,统一显示SQL语句执行时间
最近,在开发和优化一个报表型的Web项目,底层是Hibernate和MySQL. 当报表数据量大的时候,一个图表要花4秒以上的时间. 以下是我的分析和体会. 1.我首先需要知道哪些函数执行了多少时间 ...
- 一个Web报表项目的性能分析和优化实践(七):性能监测工具JavaMelody
简介 JavaMelody 能够监测Java或Java EE应用程序服务器,并以图表的方式显示:Java内存和Java CPU使用情况,用户Session数量,JDBC连接数,和http请求.sql请 ...
- PHP码农在Golang压力下的生存之道-PHP性能优化实践
随着国内Golang的火爆,phper的生存压力越来越大,在一次内部技术讨论中,gopher甚至提出,要什么php,写php的全部开掉,唉,码农何苦为难码农. 本文试图寻找一种有效实践,减少php w ...
- 携程App的网络性能优化实践
首先介绍一下携程App的网络服务架构.由于携程业务众多,开发资源导致无法全部使用Native来实现业务逻辑,因此有相当一部分频道基于Hybrid实现.网络通讯属于基础&业务框架层中基础设施的一 ...
随机推荐
- Linux部署之批量自动安装系统之DHCP篇
1. 安装:yum install dhcp 2. Ip配置信息 3. Dhcp配置文件如下 4. 配置完后检查语法是否错误 ...
- 模板层 Template
每一个 Web 框架都需要一种很便利的方法用于动态生成 HTML 页面. 最常见的做法是使用模板. 模板包含所需 HTML 页面的静态部分,以及一些特殊的模版语法,用于将动态内容插入静态部分. 说白了 ...
- [USACO16DEC]Cities and States省市
题目:洛谷P3405. 题目大意:给你一些省市的名称(大写)和所在省的名称(两个大写字母),求有多少对城市满足:A城市的名字的前两个字母等于B城市所在省的名称,且A所在省的名称等于B城市的名字的前两个 ...
- 【jQuery01】添加添加div
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- redis搭建与安装
redis提供五种数据类型:string,hash,list,set及zset(sorted set). 第一部分:安装redis 希望将redis安装到此目录 1 /usr/local/redis ...
- Android APP弱网测试问题和解决分析
最近做了一次移动APP的弱网和中断测试,接下来分享一下遇到的一些问题: 1.现象:用户登录应用时下载初始化数据,下载过程中因网速太慢点击取消并重新登录,数据初始化完成后出现重复,造成数据不一致. 原因 ...
- solr + eclipse 调试环境搭建
1: 在官网下载对应源码 http://www.fayea.com/apache-mirror/lucene/solr/4.1.0/ 选择源码文件,如图所示: 2: 解压后目录如图所示: 在根目录下存 ...
- windows 下面的grep awk 命令
windows 下面的grep awk 命令 grep 学习了:http://blog.csdn.net/chengfans/article/details/53784936 awk学习了:http: ...
- cocos2d_x_01_环境搭建
终于效果图: Cocos2d-x-3.3 Mac 安装 下载地址: 參考文档: 在线API列表: Cocos2d-x-3.3 版本号 从配置安装到创建项目都是命令行 官网下载最新版本号Cocos2 ...
- uva 10474 Where is the Marble?(简单题)
我非常奇怪为什么要把它归类到回溯上,明明就是简单排序,查找就OK了.wa了两次,我还非常不解的怀疑了为什么会 wa,原来是我居然把要找的数字也排序了,当时仅仅是想着能快一点查找.所以就给他排序了,没考 ...