这是一个远古问题,今天突然想起来记录一下。起因是有一个需求需要使用java执行一个数据量稍大(5000-10000条)的查询语句。执行时发现速度会非常非常慢~ 慢到怀疑人生,慢到怀疑断网了。。。经过了解,发现都是fectchsize参数搞的鬼~
-
解决方式
在mapper中添加了factchsize参数,将值设置为2000:(当然如果你没有使用mybatis的话,jdbc的PrepareStatement、ResultSet和Statement都提供了相应的setFetchSize方法供我们修改默认值。)
<select id="xxxxx" parameterType="xxxxx" resultMap="BaseResultMap" fetchSize="2000">
修改后执行效率提升了将近15倍
-
Why?
-
首先Oracle是一个全双工的数据库,列如在查询时并不是将查询结果一次性返回至客户端,而是每次将 fectch size 大小条数据读取到缓存中供客户端获取。这样客户端执行一次查询操作,就要跟数据库进行 totalCount/fectchsize 次交互才能完成。所以我上面的情况,大部分时间是浪费在了与数据库的网络交互上。
-
然而Mysql是不存在这样的问题的,因为mysql是半双工的数据库,默认的行为是需要把整个结果全部读取到内存中才开始允许应用读取结果,所以不支持类似Oracle factch size的参数设置。
-
以上解释纯个人理解,可能不准确。
-
再来看一下JDBC官方文档《Database JDBC Developer's Guide》的描述:
By default, when Oracle JDBC runs a query, it retrieves a result set of 10 rows at a time from the database cursor. This is the default Oracle row fetch size value. You can change the number of rows retrieved with each trip to the database cursor by changing the row fetch size value.
Standard JDBC also enables you to specify the number of rows fetched with each database round-trip for a query, and this number is referred to as the fetch size. In Oracle JDBC, the row-prefetch value is used as the default fetch size in a statement object. Setting the fetch size overrides the row-prefetch setting and affects subsequent queries run through that statement object.
Fetch size is also used in a result set. When the statement object run a query, the fetch size of the statement object is passed to the result set object produced by the query. However, you can also set the fetch size in the result set object to override the statement fetch size that was passed to it.
Changes made to the fetch size of a statement object after a result set is produced will have no affect on that result set.
文中只提到了OJDBC的FectchSize参数的相关信息比如默认值为10云云,也就侧面证实了mysql不支持fectch size参数的想法。
-
-
总结
- 总结一下,Fetch相当于读缓存,如果FetchSize设置为10000,本地缓存10000条记录,每次执行rs.next,只是内存操作,不会有数据库网络消耗,效率就会高些。但需要注意的是,Fetch Size值越高则占用内存越高,要避免出现OOM错误。具体设置多大,还需要根据自己的业务情况以及执行效率自行判断~
评论区