`
FansUnion
  • 浏览: 152455 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

一个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

分享到:
评论

相关推荐

    MyEclipse连接MySQL数据库报错解决办法

    我们现在一般网站都是利用的MySQL数据库搭建网站的,但是在网上看到很多网友吐槽数据库连接不上的问题,现在我就结合相关资料向提出一些我个人的见解,希望对大家解决问题有帮助。 一般MySQL连接不上,可能有两大...

    解决mysql 1040错误Too many connections的方法

    从官方文档知道linux上面编译安装的mysql默认的连接为100个,这样对于网站的需求来说是远远不够的。 mysql官方告诉我们需要修改max_connections的值,那么我们怎么去修改呢?有两种方法 1、修改配置文件文件 修改/etc...

    基于MySQL的数据库查询性能优化.pdf

    基于MySQL的数据库查询性能优化.pdf基于MySQL的数据库查询性能优化.pdf基于MySQL的数据库查询性能优化.pdf基于MySQL的数据库查询性能优化.pdf基于MySQL的数据库查询性能优化.pdf基于MySQL的数据库查询性能优化.pdf...

    MySQL数据库优化

    MySQL数据库优化(一) 1 MySQL数据库优化(二) 5 MySQL数据库优化(三) 13 MySQL数据库优化(五):锁 25 MySQL数据库优化(六):优化数据库结构 29 MySQL数据库优化(七):MySQL如何使用索引 31 MySQL数据库...

    一个mysql数据库连接类库.zip

    数据库连接是一种有限的昂贵的资源,数据库连接影响到程序的性能指标,数据库连接池正是针对这个问题提出来的,数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新...

    java web 项目JDBC连接MySQL数据库

    java web项目,使用JDBC连接MySQL数据库,首先得在本机的MySQL数据库中创建相应的数据库和数据表,并添加相关数据,解压后在MyEclipse中通过import向导将整个文件夹加入到工程中,然后运行即可。

    基于Docker的MySQL数据库性能分析.pdf

    基于Docker的MySQL数据库性能分析.pdf

    《Web数据库技术(第二版)》第11章:MySQL数据库

    《Web数据库技术(第二版)》第11章:MySQL数据库 本章内容主要讲MySQL数据库。

    高级MySQL笔记时,需要深入了解数据库的内部机制和优化技巧,以提高数据库性能和效率

    在进行高级MySQL笔记时,需要深入了解数据库的内部机制和优化技巧,以提高数据库性能和效率。索引优化:索引是提高数据库查询速度的重要工具。可以通过分析查询语句和表结构,合理地设计和使用索引,避免全表扫描,...

    MySQL数据库优化实践

    MySQL数据库优化实践 ,是个不错的文档,里面有很多关于mysql 优化的方法

    MySQL数据库性能优化分析.pdf

    MySQL数据库性能优化分析.pdf

    MySQL数据库查询优化

    预计时间2小时,每小时一个课程段(子查询是SQL查询优化的重点内容,务必掌握好) 第5课 查询优化技术理论与MySQL实践(三)------视图重写与等价谓词重写 什么是视图重写?哪些类型的视图可以被优化?MySQL是怎么...

    软件工程实践案例分析+报告(微信小程序+mysql数据库+web后台管理).zip

    软件工程实践案例分析+报告(微信小程序+mysql数据库+web后台管理).zip软件工程实践案例分析+报告(微信小程序+mysql数据库+web后台管理).zip软件工程实践案例分析+报告(微信小程序+mysql数据库+web后台管理)....

    mysql数据库性能优化

    mysql性能优化常用方法,以下为《构建高性能Web站点》部分内容节选,感谢您的阅读,您可以自由传播它,但请确保它的完 整性。

    EFcore连接本地Mysql数据库使用ASP.NET.CORE.API

    1.实现EFcore连接本地Mysql数据库 2.最新版本的ASP.NET.CORE.Web API 3.实现dbfrist和code 。 4.博客链接:https://blog.csdn.net/EAyayaya/article/details/124048491 5. 不是MVC方式 6.Entity Framework Core ...

    Unity3D教程:两种方法连接MySql数据库2

    Unity3D教程:两种方法连接MySql数据库 Posted on 2013年06月27日 by U3d / Unity3D 基础教程/被围观 14 次 1、

    两小时解决所有问题--MySQL数据库性能优化

    两小时解决所有问题--MySQL数据库性能优化

    c语言连接mysql数据库

    在我们做C项目用到mysql数据库时需要进行和mysql进行连接,而连接mysql的步骤中几乎时固定的,因此可以写在文件中保存以备用。(文件中main函数中两个printf函数的输出为测试结果,调用连接函数前为NULL,调用连接...

    MySQL数据库习题和实践答案.rar

    MySQL数据库习题和实践答案.rar

    一个mysql数据库连接类

    这是一个比较常用的mysql+php的数据库连接类文件代码,这款连接类支持pconnect与mysql_connect来连接,用户可以根据自己的WEB应用类型来处理作选择。

Global site tag (gtag.js) - Google Analytics