MySQL连接过多

3

我不想提出一个在网络上被广泛询问的问题,但我似乎无法解决它。

一段时间以前,我开始了一个项目,在经过一个月的测试后,我遇到了“太多连接”的错误。我研究了一下,通过增加max_connections来“解决”了这个问题。然后它就正常工作了。

自那时以来,越来越多的人开始使用它,然后又遇到了同样的问题。当我是网站上唯一的用户时,我键入“show processlist”,它显示还有大约50个连接处于打开状态(命令中显示为“Sleep”)。现在,我不知道为什么这些连接还打开着,但在我的代码中,我三番五次地检查每个我打开的连接都已关闭。

例如:

public int getSiteIdFromName(String name, String company)throws DataAccessException,java.sql.SQLException{

Connection conn = this.getSession().connection();
Statement smt = conn.createStatement();
ResultSet rs=null;
String query="SELECT id FROM site WHERE name='"+name+"' and company_id='"+company+"'";

rs=smt.executeQuery(query);
rs.next();

int id=rs.getInt("id");

rs.close();
smt.close();
conn.close();
return id;
}

每当我在网站上做其他事情时,就会打开另一批连接并未关闭。我的代码有问题吗?如果没有,可能是什么问题?

这个 this.getSession().connection() 是做什么的? - Tahir Akhtar
你的应用程序中只有这个地方打开连接吗? - Tahir Akhtar
你是否将所有的异常记录在某个地方? - Tahir Akhtar
4个回答

11

如果在调用conn.close()之前出现任何异常,你的做法将永远不会关闭连接。你需要在try块中获取连接(以及语句和结果集),并在finally块中关闭它。无论是否抛出异常,finally中的代码总是会执行。通过这种方式,你可以确保昂贵的资源被关闭。

public int getSiteIdFromName(String name, String company) throws DataAccessException, java.sql.SQLException {
    Connection conn = null;
    Statement smt = null;
    ResultSet rs = null;
    int id = 0;
    try {
        conn = this.getSession().connection();
        smt = conn.createStatement();
        String query = "SELECT id FROM site WHERE name='" + name + "' and company_id='" + company + "'";
        rs = smt.executeQuery(query);
        rs.next();
        id = rs.getInt("id");
    } finally {
        if (rs != null) try { rs.close(); } catch (SQLException logOrIgnore) {}
        if (smt != null) try { smt.close(); } catch (SQLException logOrIgnore) {}
        if (conn != null) try { conn.close(); } catch (SQLException logOrIgnore) {}
    }
    return id;
}

尽管如此,这段代码容易受到SQL注入 攻击。请使用PreparedStatement代替Statement

另请参阅:


1
BalusC提供了另一个信息丰富且描述详细的答案,非常感谢。我会在整个网站上尝试一下,看看它是否解决了问题。 - MichaelMcCabe

2

这段代码可能会泄露连接的一种可能流程是:

  1. Stmt.executeQuery() 返回空的结果集
  2. 您没有检查 rs.next() 是否返回 true 或 false
  3. rs.getInt("id") 抛出异常,因为结果集中没有当前行
  4. conn.close() 被跳过

请执行以下操作:

  1. 将 rs.getInt() 的执行条件设置为 rs.next()
  2. 在 finally 块中关闭连接,并在 try 块中进行所有数据访问

编辑:

此外,将所有异常记录在某个地方也是一个好主意,这样您就可以在故障排除时有一个良好的起点。


0
如果代码抛出DataAccessException或java.sql.SQLException,连接将不会关闭,导致许多打开的睡眠连接;) 请尝试使用try-finally块来关闭连接。
Connection conn = this.getSession().connection();
try {
  // all code
} finally {
  rs.close();
  smt.close();
  conn.close();
}

这是一个微不足道的例子,但稍微复杂一些,因为您需要检查哪些对象实际上已被创建和使用。


0

这个问题的临时解决方案可以是增加mysql允许的最大连接数。您可以通过以下两种方式之一来实现。

第一种:

登录到mysql服务器并输入下面给出的命令。

mysql> SET GLOBAL max_connections = 200;

这将增加最大连接数。但是如果服务器重新启动,此设置将更改。

第二步:

编辑文件 /etc/mysql/my.cnf 并在其中增加 max_connection。

[mysqld]
local-infile=0
datadir=/var/lib/mysql
user=mysql
symbolic-links=0

max_connections = 100

保存更改并输入以下内容以重新启动mysqld:

/etc/init.d/mysqld restart

但是这个问题通常出现在未正确关闭并且无法使用的开放连接。

请尝试查看所有访问您的数据库的资源,并检查是否已正确处理所有连接。

查看代码可以发现,您没有将代码放置在 try 和 catch 中。

根据您的代码,如果在获取数据时出现异常,您的连接将不会被关闭,因此它将成为资源的浪费。因此,如编码标准建议的那样,请使用 try 和 catch 与 finally 来处理连接。

try{
//your code 
}
catch(Exception e){

//handle the exceptions here
}

finally{
        try{
            channel.close();
        } catch(Exception e){
            log.error("Error is "+e.getMessage(),e);
            e.printStackTrace();
        }
        try {
            connection.close();
        } catch (IOException e) {
            log.error("Error is "+e.getMessage(),e);
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch(Exception e){
            log.error("Error is "+e.getMessage(),e);
            e.printStackTrace();
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接