前几天有个小伙伴说他有个疑问:当我们发起一个查询的时候,数据库服务器是把所有结果集都准备好,然后一次性返回给应用程序服务吗(因为他们生产有个服务因为一个报表查询搞宕机了)。
这样想的原因很简单,假设那个报表查询出来有几百万数据,然后一次性倾泻给应用程序了,应用没那么大内存空间,而且同时还要兼顾处理其他请求,直接就导致内存溢出,此服务不可用了。~~~最有效的办法当然时优化这种报表SQL。
- 说回他的疑问,真的时一次性返回所有数据吗,答案是不一定。在JDBC的规范中并没有规定与DBMS交互过程中,每次返回多少记录数据,而是在Statement、ResultSet接口中提供了setFetchSize方法让具体的数据库厂商自行实现每次返回的结果集。或者不设置的时候数据库厂商也许会实现使用流式处理方式读取数据
主要是用来减少数据库服务和应用查询服务之间网络交互次数,不至于一次值返回一行让客户端接受解析,也避免大数据传输时一次过多的数据而影响性能。 -
当设置了FetchSize时,服务器每次返回设置大小的结果集给客户端缓存起来,然后客户端使用ResultSet的next()方法从本地缓存中读取并解析和填充这些数据。之后再次next()时再重新从数据库服务器请求获取数据,就像游标一样,读取下一个游标
不同数据库的不同默认实现:
1. oracle的实现(比如ojdbc6),ojdbc中默认的fetchSize是10
2. mysql中因版本不同也有差异,MySQL Connector/J 5.1.13及更高版本中,对Statement对象,默认的Fetch Size被设置为Statement.DEFAULT_FETCH_SIZE,其值为0,这意味着驱动程序将尝试使用最佳性能策略,通常采用流式处理方式从服务器端按需读取数据,而不是一次性加载所有数据。而比较早的版本,可能一次性加载所有结果
3. SQLServer,也就是sqljdbc的实现,从6.0开始支持这个设置。默认值设置为-1。这个值表示驱动程序会尝试使用服务器端的最佳性能策略来处理结果集,而不是一次性加载所有数据到客户端。这意味着在大多数情况下,它会采用流式读取的方式。
常用的数据持久化框架都支持设置,
比如Mybatis:
<select id="queryOrder" fetchSize="200"> SELECT * FROM my_tb </select>
或者高版本的注解实现:3.4.1以上
@Select("SELECT * FROM my_tb")
@Options(fetchSize = 200)
List