日期:2014-05-16  浏览次数:20429 次

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=