【问题标题】:Mysql inserts in stored procedure slow over Java JDBCMysql 在存储过程中插入速度比 Java JDBC 慢
【发布时间】:2012-07-24 14:18:52
【问题描述】:

与从 IDE 或 mysql 命令行调用相比,我发现通过 JDBC 调用 mysql 插入时间要慢几个数量级,我想知道是否有人能指出我正确的方向。

我在本地运行所有内容,因此网络延迟应该不是问题。

我创建了以下非常人为的示例来说明这一点。

我创建了一个存储过程,它接受单个 INT 并将其插入到表中:

DELIMITER $$

CREATE PROCEDURE testInsert(IN input INT)
BEGIN
        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

    END$$

DELIMITER ;

如果我要从命令行或 mysql IDE(我正在使用 SQLyog)调用此过程:

CALL testInsert(0);

出于所有意图和目的,过程调用立即发生:

Execution Time : 0.007 sec
Transfer Time  : 0.001 sec
Total Time     : 0.009 sec

但是,如果要从 Java 调用该过程,请使用以下代码:

public void testStoredProcedureInsert() throws SQLException{
    //custom class for managing the sql and variables on the prepared statement
    SQLStatementBinder sqlBinder = new SQLStatementBinder();
    sqlBinder.appendSql("CALL testInsert(?)");

    //will store the object into a collection in the sql binder
    sqlBinder.addBindVariable(0);

    Class.forName("com.mysql.jdbc.Driver").newInstance();

    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbname", "user", "pass");

    CallableStatement stmt = connection.prepareCall(sqlBinder.getSQL());

    //bindSql loops through bind variables and sets the appropriate parameters based on the type of the objects - in this case only preparedStatement.setInt(1, 0) is called.
    stmt = (CallableStatement)sqlBinder.bindSql(stmt);

    long start = System.currentTimeMillis();

    stmt.executeQuery();

    long end = System.currentTimeMillis();

    System.out.println("time to call procedure - " + (end-start) + "ms");
}

更新的运行时间(请注意,我只对实际执行进行计时,因此不考虑连接开销等)平均约为 70 毫秒

为了进一步说明这一点,如果我在存储过程中连续插入多个插入:

DELIMITER $$

CREATE PROCEDURE testInsert(IN input INT)
BEGIN
        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

        INSERT INTO test_table(column_1, column_2) 
        VALUES(input, input);

    END$$

DELIMITER ;

从 SQLyog 调用仍然很快:

Execution Time : 0.040 sec
Transfer Time  : 0.003 sec
Total Time     : 0.044 sec
---------------------------------------------------

现在调用上面相同的 java 代码平均每个过程调用 225ms

我希望我做错了一些微不足道的事情,但我愿意接受任何我应该测试的建议或事情。还是从 java 调用时有那么多额外的开销?

抱歉,帖子有点长,但我试图提供大量信息。感谢您的帮助!

更新只是另一种情况,如果我将五个插入移动到一行中:

 DELIMITER $$

    CREATE PROCEDURE testInsert(IN input INT)
    BEGIN
            INSERT INTO test_table(column_1, column_2) 
            VALUES(input, input),(input, input),(input, input),(input, input),(input, input);

        END$$

    DELIMITER ;

java 运行时平均为 80ms,这表明从 java 调用过程中的每个单独语句似乎都有一些额外的开销。

调用没有插入的其他过程,即使是那些更复杂并返回几个不同结果集的过程,所花费的时间也比一些简单的插入要少得多。只有选择的过程会很快返回,但是在过程中添加了一个插入,并且运行时比预期的要慢得多(同样,只是来自 java - 而不是来自 SQLyog)。

【问题讨论】:

    标签: java mysql stored-procedures jdbc mysql-connector


    【解决方案1】:

    好的 - 几个小时后,我随机决定尝试将表上的引擎从 InnoDB 更改为 MyISAM,这似乎显着加快了插入速度。

    我想我将不得不对这两个引擎之间的差异进行更多审查,除了缺少 FK 和 MyISAM 中的事务。

    至少是进步。

    【讨论】:

      【解决方案2】:

      尝试改用 JDBC 连接池并检查时间戳 - 它应该更低(尽管与 MySQL 插入不同)。 还要检查用于插入 MySQL 的数据类型以及是否可以以某种方式进行优化。

      【讨论】:

      • 连接池是否会对单个调用产生重大影响?你有首选的连接池库或一些好的例子吗?
      • 是的,它可以 - 您应该比较在正常负载和重负载下获得连接之前和之后的时间戳。随着负载的增加,根据您配置和使用池的方式,使用连接池应该会做得更好。作为参考,您可以查看用于 tomcat (tomcat.apache.org/tomcat-7.0-doc/…) 的连接池和代码 - 还有很多其他可用选项(如果您使用的是应用程序服务器/servlet 容器 - 大多数都带有连接池)跨度>
      • @MrZorn 很高兴阅读Connection Pooling。引用第一条语句:“如果您使用 SQL 或其他类似工具连接到数据库并处理数据,您可能知道获取连接和登录是最耗时的部分。应用程序可以轻松每次需要建立连接时花费几秒钟...如果您使用的是 2.0 之前的 JDBC 版本并希望提高性能,则可以改为缓存 JDBC 连接。”
      • 我将研究连接池 - 但我的 Java 计时直到连接创建后才开始(至少据我所知)。此外 - 调用没有插入的其他过程,即使是那些更复杂并返回几个不同结果集的过程,所花费的时间也比一些简单的插入要少得多。
      猜你喜欢
      • 2012-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 2012-08-04
      • 2018-07-14
      • 1970-01-01
      相关资源
      最近更新 更多