【问题标题】:C3P0 + MySQL : Got an error reading communication packetsC3P0 + MySQL:读取通信数据包时出错
【发布时间】:2011-06-26 23:56:24
【问题描述】:

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

[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);
            }
}

整个程序(对我而言)似乎是合法的。根据http://dev.mysql.com/doc/refman/5.0/en/communication-errors.html 的 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)

【问题讨论】:

    标签: java mysql c3p0


    【解决方案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();
    }
    

    请注意,在 100 秒的时间段结束(并且数据源工厂关闭)之前,日志中不会出现任何警告!所以,是datasource.close() 导致了他们……

    【讨论】:

      【解决方案2】:

      好吧,很高兴看到一个包含适量信息的问题,这些信息可以暗示系统可能遇到问题的地方。这是我要检查的内容:

      • 在关闭Connection 对象之前尝试关闭PreparedStatement 对象,并验证这是否已解决问题。这听起来可能没有必要,但在某些 JDBC 驱动程序的情况下是必需的,尤其是 Oracle(也可能是 MySQL)。基本原理是 Connection 对象并没有真正关闭,特别是如果像 ResultSetStatement 对象这样的派生对象还没有首先关闭(按照提到的顺序)。部分原因在于 JDBC 驱动程序的编写方式。
      • 验证所涉及的机器是否能够真正处理您希望它们处理的负载。如果底层网络基础设施根本无法处理指定数量的连接,那么连接很可能会被丢弃。如果第一个建议没有帮助,您可能需要查看 Wireshark 转储并从中得出推论。
      • 使用debugUnreturnedConnectionStackTraces 标志来检测任何连接池泄漏。这也要求 unreturnedConnectionTimeout 为正数。该标志确保为应用程序不将连接返回池的情况提供堆栈跟踪。堆栈跟踪将指示代码中最初保留连接的位置。

      【讨论】:

      • 准备好的语句在连接前关闭。然而两者都关闭了。关于负载,我运行了一个只涉及一个 INSERT 语句的单元测试,因此这里不会超过服务器可以处理的连接数。
      • @Pantelis Sopasakis,连接池的最小大小设置为 50。我不确定您的单元测试是否真的在检查这个。此外,我更新了答案以反映调试标志的使用,该标志可能有助于检测连接池使用固有的连接池泄漏的可能性。
      • 非常感谢或帮助!问题现已解决:-) 我现在正在发布答案。
      猜你喜欢
      • 1970-01-01
      • 2018-08-03
      • 2022-10-05
      • 1970-01-01
      • 1970-01-01
      • 2020-02-04
      • 2019-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多