C3P0 + MySQL:读取通信包时出错

时间:2011-02-14 15:11:36

标签: java mysql c3p0

许多人遇到的错误与消息有关:

[Warning] Aborted connection 38 to db: 'database_name' user: 
'root' host: 'localhost' (Got an error reading communication packets)

,可在MySQL日志中找到。就我而言,使用驱动程序com.mysql.jdbc.Driver和众所周知的C3P0池,通过Java客户端访问本地数据库。我的MySQL服务器配置为接受大量连接,max_allowed_pa​​cket值设置为64M。以下是我的my.cnf文件(MySQL配置)的摘录:

[mysqld]
max_allowed_packet  = 64M
thread_concurrency      = 8
thread_cache_size       = 8
thread_stack        = 192K
query_cache_size = 0
query_cache_type = 0
max_connections = 1024
back_log = 50
innodb_thread_concurrency = 6
innodb_lock_wait_timeout = 120
log_warnings

[mysqldump]
quick
quote-names
max_allowed_packet  = 64M

我的数据库中的表User具有以下简单结构:

CREATE TABLE `User` (
  `uid` varchar(255) COLLATE utf8_bin NOT NULL,
  `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `mail` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `password` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `maxParallelTasks` tinyint(4) NOT NULL DEFAULT '10',
  `maxModels` int(11) NOT NULL DEFAULT '2000',
  `maxBibTeX` int(11) NOT NULL DEFAULT '2000',
  PRIMARY KEY (`uid`) USING BTREE,
  UNIQUE KEY `mail` (`mail`) USING BTREE,
  KEY `uid` (`uid`) USING BTREE,
  KEY `index_user_name` (`name`) USING BTREE,
  KEY `index_user_mail` (`mail`) USING BTREE,
  KEY `index_user_maxModels` (`maxModels`) USING BTREE,
  KEY `index_user_maxBibTeX` (`maxBibTeX`) USING BTREE,
  KEY `index_user_maxParallelTasks` (`maxParallelTasks`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

当我INSERT使用mysql客户端(即mysql -u root -p)表中的值时,我在日志中没有收到任何警告。但是,当尝试相同的Java端时,上述警告会出现在日志中很多次!所以,在Java端,我的连接是从C3P0连接池中提取的,配置如下:

datasource = new ComboPooledDataSource();
datasource.setJdbcUrl(connectURI);
datasource.setMaxPoolSize(1000);
datasource.setMinPoolSize(20);
datasource.setInitialPoolSize(50);
datasource.setNumHelperThreads(6);
datasource.setTestConnectionOnCheckin(true);
datasource.setTestConnectionOnCheckout(true);

该声明首先准备好了:

PreparedStatement ps = connection.prepareStatement(getSql());

然后参数化:

ps.setString(1, user.getUid());
ps.setString(2, user.getName());
ps.setString(3, user.getMail());
ps.setString(4, user.getHashedPass());

然后执行:

int update = ps.executeUpdate();

然后关闭准备好的声明:

ps.close();

并且SQL连接已关闭:

if (connection != null) {
            try {
                if (!connection.isClosed()) {
                    connection.close();
                }
            } catch (SQLException ex) {
                throw new DbException(ex);
            }
}

整个程序似乎(对我来说)是合法的。根据{{​​3}}的MySQL手册,此警告可能意味着:

  

客户端程序没有调用   退出之前的mysql_close()。

或那:

  

客户端程序突然结束   数据传输的中间。

出于我的应用程序的目的,我已经使用C3P0使用200多个并行线程在这个数据库中进行写入和读取,并且在检查数据实际上已经转移到数据库并从数据库中检索时我没有内存泄漏。还有其他人有类似的经历吗?

最后,我提供了我的MySQL版本以及可能对故障排除有用的其他信息:

mysql> show variables like "%version%";
+-------------------------+-------------------+
| Variable_name           | Value             |
+-------------------------+-------------------+
| protocol_version        | 10                | 
| version                 | 5.1.37-1ubuntu5.5 | 
| version_comment         | (Ubuntu)          | 
| version_compile_machine | x86_64            | 
| version_compile_os      | debian-linux-gnu  | 
+-------------------------+-------------------+

Java version: 1.6.0_22, (build 1.6.0_22-b04)

2 个答案:

答案 0 :(得分:1)

嗯,很高兴看到一个带有恰当数量信息的问题,这些信息会暗示系统可能遇到问题的地方。这是我要检查的内容:

  • 在关闭PreparedStatement对象之前尝试关闭Connection对象,并验证是否已解决问题。这可能听起来没必要,但在某些JDBC驱动程序的情况下是必需的,最明显的是Oracle(也可能是MySQL)。理由是Connection对象并未真正关闭,特别是如果ResultSetStatement对象之类的派生对象没有先关闭(按照提到的顺序)。在某种程度上,这是由于JDBC驱动程序的编写方式。
  • 验证所涉及的机器是否可以实际处理您希望它们的负载。如果底层网络基础架构根本无法处理指定数量的连接,则很可能会断开连接。如果第一个建议无效,你可能想查看一个wireshark转储并从中得出推论。
  • 使用debugUnreturnedConnectionStackTraces标志检测任何连接池泄漏。这也要求unreturnedConnectionTimeout为正。该标志确保为应用程序未将连接返回池的条件提供堆栈跟踪。堆栈跟踪将指示最初保留连接的代码中的点。

答案 1 :(得分:1)

尝试深入探究此警告的原因......

首先,我注意到我在日志文件中获得的警告消息数量与datasource.setMinPoolSize(int);指定的最小池大小相等或非常接近,而为了测试而设置的初始池大小是每个时间等于最小尺寸。

此外,使用调试器并逐步执行语句,我有时间观察单元测试完成后会立即记录警告。这些事实使我得出结论,C3P0汇集的连接实际上在被丢弃之前并未关闭。需要的是,一旦不再使用方法com.​mchange.​v2.​c3p0.​impl.​AbstractPoolBackedDataSource#close(),就会关闭数据源。当然,仅在单元测试结束时才这样做,所以在我的情况下,修正案看起来像这样:

@AfterClass
public static void tearDownClass() throws Exception {
  // Close the pool 
  DataSourceFactory.getInstance().close();
}

在我的应用程序中,我添加了一个关闭钩子,以便在执行停止之前关闭数据源。在此之前,池化连接保持不变,驱动程序必须关闭它们,从而在MySQL日志中引发警告。这解决了所有INSERT操作的问题,但不幸的是,不是SELECT的操作。请注意,我关闭了所有结果集,语句,预准备语句和连接,并使用FindBugs(静态分析工具)检查了我的源代码。解决方法让我怀疑C3P0再次出现问题,但我无法确定。所以以下内容使警告消失......

@AfterClass
public static void tearDownClass() throws Exception {
  Thread.sleep(100000); // wait for 100 secs
  DataSourceFactory.getInstance().close();
}

请注意,在100secs结束期间(数据源工厂关闭)之前,日志中不会显示任何警告!所以,datasource.close()会导致他们......