Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 调查JDBC和MySQL中的慢速简单查询_Java_Mysql_Performance_Jdbc_Hikaricp - Fatal编程技术网

Java 调查JDBC和MySQL中的慢速简单查询

Java 调查JDBC和MySQL中的慢速简单查询,java,mysql,performance,jdbc,hikaricp,Java,Mysql,Performance,Jdbc,Hikaricp,PreparedStatement.executeQuery()的执行时间比直接通过shell运行要长约20倍。我已经记录了计时器,以确定此方法是罪魁祸首 查询和一些数据库信息(暂时忽略Java问题): mysql>从user_id=1的用户中选择用户名//闪电般的快 通过mysqlslap运行同样的查询1000次也非常快 mysqlslap --create-schema=mydb --user=root -p --query="select username from phpbb_

PreparedStatement.executeQuery()
的执行时间比直接通过shell运行要长约20倍。我已经记录了计时器,以确定此方法是罪魁祸首

查询和一些数据库信息(暂时忽略Java问题):

mysql>从user_id=1的用户中选择用户名//闪电般的快

通过mysqlslap运行同样的查询1000次也非常快

mysqlslap --create-schema=mydb --user=root -p --query="select username from phpbb_users where user_id = 1" --number-of-queries=1000 --concurrency=1

Benchmark
        Average number of seconds to run all queries: 0.051 seconds
        Minimum number of seconds to run all queries: 0.051 seconds
        Maximum number of seconds to run all queries: 0.051 seconds
        Number of clients running queries: 1
        Average number of queries per client: 1000
问题:在JDBC中执行相同的查询会大大降低速度。在for循环中,调用下面的
queryUsername()
1000次(这是在Main方法中调用的,这里没有显示)大约需要872ms。慢了约17倍!我通过在不同的位置放置计时器(为了简洁起见省略了一些)来追踪大量使用。主要嫌疑犯是
stmt.executeQuery()
,它占用了872ms运行时间的776ms

public static String queryUsername() {
        String username = "";
        // DBCore.getConnection() returns HikariDataSource.getConnection() implementation exactly as per https://www.baeldung.com/hikaricp
        try (Connection connection = DBCore.getConnection(); 
            PreparedStatement stmt = connection.prepareStatement("SELECT username from phpbb_users where user_id = ?");) {
            stmt.setInt(1, 1);  // just looking for user_id 1 for now
            // Google timer used to measure how long executeQuery() is taking
            // Another Timer is used outside of this method call to see how long
            // total execution takes. 
            // Approximately 1 second in for loop calling this method 1000 times
            Stopwatch s = Stopwatch.createStarted(); 
            try (ResultSet rs = stmt.executeQuery();) {
                s.stop(); // stopping the timer after executeQuery() has been called
                timeElapsed  += s.elapsed(TimeUnit.MICROSECONDS);
                while (rs.next())
                {
                    username = rs.getString("username"); // the query returns 1 record
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
        return username;
    }
其他上下文和尝试的内容

  • SHOW OPEN TABLES
    打开了多个表,但所有表都有In_use=0和Name_locked=0
  • 显示完整的进程列表
    看起来很健康
  • 用户id是索引主键
  • 该服务器是一个运行Ubuntu20.04.1LTS(GNU/Linux5.4.0-66-generic x86_64)的Upcloud$5/月1核1GB内存。Mysql版本8.0.23-0ubuntu0.20.04.1,适用于x86_64((Ubuntu))上的Linux
  • JDBC驱动程序是mysql-connector-java_8.0.23.jar,它是通过mysql-connector-java_8.0.23-1ubuntu20.04_获得的

不要每次都重新连接。在开始时打开连接;重复使用它,直到网页(或程序)完成。

将借用连接和秒表相关代码移出方法。然后测量为:

 Stopwatch s = Stopwatch.createStarted();
 try (Connection con = ....)  {
     for (int i=0; i < 1000; i++)  {
            queryUsername(   con   );
      }
 }
 s.stop();
 print s.elapsed(TimeUnit.MICROSECONDS);
Stopwatch s=Stopwatch.createStarted();
try(连接con=…){
对于(int i=0;i<1000;i++){
queryUsername(con);
}
}
s、 停止();
打印时间(时间单位:微秒);

很可能你正在比较不同的现实

在运行mysqlslap时,您最有可能在工具和MySQL服务器之间的通信中使用Unix域套接字。尝试将其更改为TCP,您应该会立即看到性能下降。另一方面,Connector/J默认情况下创建基于TCP的连接(可以使用Unix域套接字,但只能使用第三方库)

此外,在mysqlslap中,您直接运行一个简单的查询,该查询由COM_query protocol命令处理。在Java示例中,您首先准备查询,然后执行它。根据连接器/J的配置方式,这可能导致一个COM_查询协议命令或一对命令,即COM_STMT_PREPARE和COM_STMT_EXECUTE。连接器/J还受其语句缓存(和/或CP缓存)配置方式的影响。但是,您只测量
executeQuery
部分,因此,理论上,连接器/J在这里可能更受欢迎


最后,除非你真的提出一个用例,保证两个执行在相同的环境下有效地完成相同的工作,否则你可以比较结果并指出差异,但你不能从中得出任何结论。例如,引入缓存并使那些简单的迭代完全跳过与服务器的通信并不难。。。这会让事情变得非常快。

或者可能使用DBCP来完成一个严肃的程序。我的想法很复杂。通过HikariDataSource.getConnection()获取连接对象。这是不对的吗?(上面的DBCore.getConnection()就是这么做的)我希望DBCP需要一些时间。将其留给连接到相同或不同网页的多个用户。您的测试选择尽可能简单。(
SELECT 1
更简单。)您所做的似乎是为连接计时,而不是
SELECT
。我误读了代码吗?循环在哪里?Slap将整个过程运行1000次?OP正在使用连接池(HikariCP),我猜它的名字是这样的,所以本质上他们已经在重用连接了。这已经被测量过了。花了872毫秒。大部分时间来自queryUsername()方法。根据872ms的OP 776ms,运行时来自queryUsername()。与使用通过HikariDataSource池获得的连接相比,使用单个连接没有任何改进。请检查DBCore.getConnection()以查看为连接设置的任何选项。。例如事务隔离级别、自动提交、只读、模式、各种缓存选项。。。在运行期间,jdbc和MySQLSLAPSMONITOR GC活动的某些默认设置可能不同。另外,使用的度量可能是——迭代,而不是——mysqlslap要使用的查询数?非常感谢您在这里的想法。我已经实现了Unix域套接字库
junixsocket
,以比较性能。计算时间与Hikari(TCP)相同。Mysqlslap仍然运行得非常快。我有点被难住了。我们在JDBC中实现了每个查询约1ms,而在slap中每个查询约.3ms。我还继续在slap查询中添加了--protocol=tcp标志,并且时间没有改变。我开始认为,要将JDBC的1ms时间减少到我们在slap中看到的0.3ms是不可能的,但也许你有一些想法。你怎么看?我不能为JUnitSocket说话,但我预计Connector/J与mysql命令行工具相比会有所放缓。后者是用C实现的,并使用
libmysqlclient
,因此它们更接近“裸机”。您说添加
--protocol=tcp
不会改变结果,但这不是我看到的:
运行所有查询的平均秒数:1.004秒
vs
运行所有查询的平均秒数:0.137秒