【开源项目】震惊JDBC查询比MyBatis查询慢
创始人
2024-03-26 14:05:56
0

震惊JDBC查询比MyBatis查询快?

文章编写起始原因,在编写项目的时候碰到一个深坑,JDBC获取5000条数据,居然耗时261s,MyBatis同样的操作,耗时12s左右,震惊。看到这里下巴都快掉下来了。不是网上都说MyBatis就是封装了的JDBC吗?不是都说JDBC比MyBatis快很多很多吗?为啥这里却别这么大?

先上图,看看查询的具体情况吧
请添加图片描述

具体的代码如下:

private List selectTransferData() {List list = new ArrayList<>();ResultSet rs;Statement stmt;Connection newConn;try {newConn = druidDataSource.getConnection();stmt = newConn.createStatement();stmt.setFetchSize(Integer.MIN_VALUE);rs = stmt.executeQuery(sql);} catch (SQLException e) {logger.error("traceId: {}, mysql executeQuery error!!! error msg: {}", traceId, e.getMessage());throw new DataMigrationException(DataMigrationConstant.MYSQL_EXECUTE_QUERY_ERROR);}String[] cloumnSplit = transferInsertConfig.getTableCloumnName().split(",");try {while (rs.next()) {StringBuilder data = new StringBuilder("(");for (int i = 0; i < cloumnSplit.length; i++) {String cloumnName = cloumnSplit[i].replaceAll(" ", "");String string = rs.getString(cloumnName);data.append(string).append(", ");}String dataStr = data.substring(0, data.length() - 2) + ")\r\n";list.add(dataStr);}} catch (Exception e) {logger.error("traceId: {}, 获取查询结果集异常!!! error msg: {}", traceId, e.getMessage());throw new DataMigrationException("获取查询结果集异常, 异常信息:" + e.getMessage());} finally {try {if (rs != null) {rs.close();}if (stmt != null) {stmt.close();}if (newConn != null) {newConn.close();}} catch (Exception e) {logger.error("traceId: {}, close io error!!! error msg: {}", traceId, e.getMessage());}}return list;
}

代码里面执行的SQL如下:

SELECT * FROM books WHERE id > 3000000 LIMIT offset, length;
-- offset: 150000 start
-- length: 5000

由于代码执行过慢,这里对代码进行了添加日志观察,看看到底慢在了哪里。根据网上查询的资料来看,应该就是慢在了SQL,实际跟踪下来的结果如下:

关键代码执行时间预期结果实际结果备注
newConn = druidDataSource.getConnection();30ms很快符合预期结果
rs = stmt.executeQuery(sql);12.6s可以接收的慢不符合260s的预期
while (rs.next()) { … }252s超出预料了

根据这个结果,又在网上找了很久,基本总结下来就就是SQL慢,rs.next()慢是错觉。然后博主又做了10组sql的查询结果如下:

SQL执行时间表数据量
SELECT * FROM books WHERE id > 3000000 LIMIT 300000, 500012.576s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 305000, 500012.879s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 310000, 500015.952s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 315000, 500015.541s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 320000, 500013.110s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 325000, 500012.691> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 330000, 500016.411s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 335000, 500011.809s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 340000, 50009.461s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 345000, 500010.374s> 350w

直接执行SQL和上面日志跟踪到的执行时间出入不大,于是准备使用mybaits来看测试看看,测试代码如下

IntStream.range(1, 11).forEach(it -> {long l = System.currentTimeMillis();booksMapper.getLimit(it * 5000 + 300000);long l1 = System.currentTimeMillis();System.out.println("耗时:" + (l1 - l) / 1000 + "s");
});List getLimit(int offset);

执行结果如下:

SQL执行时间表数据量
SELECT * FROM books WHERE id > 3000000 LIMIT 305000, 500010.301s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 310000, 500011.130s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 315000, 500012.417s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 320000, 500013.588s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 325000, 500013.810s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 330000, 500013.968s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 335000, 500018.056s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 340000, 500011.202s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 345000, 500018.017s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 350000, 500016.619s> 350w

看到这里的时候跟JDBC对比,发现如果仅仅只是SQL执行跟MyBatis比较,真的没有很大出入。但是MyBatis其实不仅查询了数据,而且还做了数据的封装并且返回。JDBC这里也是做了封装的,为啥差别这么大。真的是代码问题吗?好像结果已经不言而喻了。博主还是不太敢相信,决定在调整一下代码,调整代码如下:

while (rs.next()) {// 删除了处理逻辑,这样就不会是封装返回值影响了
}

实际调用结果如下:

执行第几次执行时间表数据量
1200s> 350w
2180s> 350w
3165s> 350w
4185s> 350w
5193s> 350w
6184s> 350w
7178s> 350w
8162s> 350w
9180s> 350w
10191s> 350w

这是不是已经铁的事实,就是拼接问题?不见得吧。比较下来,拼接问题看来并不是很大。耗时还是再rs.next上。

rs.next()的实现就是光标的移动,光标的移动仅仅只是位置的偏移而已,并不产生很多的消耗。这个时候只能提出假设了,假设rs.next并不仅仅是走了光标而且还在数据库内做了什么,并且与我们的SQL有关,那我们SQL本身很慢的话,那这种现状就有解释。于是对SQL调整,调整如下:

SELECT * FROM books a INNER JOIN( SELECT * FROM books WHERE id > 3000000 LIMIT 255000, 5000 ) b ON a.id = b.id

先SQL执行测试10组试试:
执行结果如下:

SQL执行时间表数据量
SELECT * FROM books WHERE id > 3000000 LIMIT 305000, 50004.662s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 310000, 50004.966s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 315000, 50004.407s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 320000, 50004.276s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 325000, 50004.199s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 330000, 50004.422s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 335000, 50004.282s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 340000, 50003.819s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 345000, 50003.813s> 350w
SELECT * FROM books WHERE id > 3000000 LIMIT 350000, 50003.666s> 350w

看起来快了比之前SQL执行快了两倍还要多。不过这里有个问题,我们执行SQL的时候,如果是在客户端里面执行的,他会自动限定查询的返回结果,比如:固定显示200条,其实就是limit 200,这个条件导致我们测试不是那么的准。所以还是继续走程序

先将之前的代码恢复到最开始的状态,然后用这个调整的SQL走10组试试

执行第几次执行时间表数据量
1629.969s> 350w
2607.075s> 350w
3660.476s> 350w
4653.261s> 350w
5679.521s> 350w
6611.962s> 350w
7598.981s> 350w
8604.788s> 350w
9651.673s> 350w
10650.522s> 350w

事实证明,就是优化的SQL在客户端执行看似有用,实际程序内运行,慢了3倍。

JDBC查询比MyBatis慢在哪里?

真的是rs.next吗?

------------------------------------> 待续

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...