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秒