1.产生背景
默认情况下,jdbc底层驱动加载数据的时候,是加载所有的sql结果集。比如select * from table,这个sql将会一次性加载所有的表中数据到jvm内存中去,如果数据少还没关系,如果数据量大的话,那么势必会出现jvm内存溢出。
同时,这个问题也是我参加面试的时候,面试官问到我的问题,由于之前并没有很深入的研究jdbc底层,一直认为,默认情况下jdbc底层就是通过流去加载(next()一次加载一条到内存,这种想法是错的)
2.jdbc底层怎么控制jvm内存溢出
jdbc通过两个参数来保证
useCursorFetch--使用游标方式提取
fetchSize--每次提取的大小。也就是一次与数据库服务器通信获取的数据量
这两个参数是关键参数。参数的设置方式
useCursorFetch通过jdbc url参数设置,比如
jdbc:mysql://192.168.5.240:3306/turbo_2?useCursorFetch=true
fetchSize通过Statement或PrepareStatement设置,比如:
PreparedStatement pst= conn.prepareStatement(sql);
pst.setFetchSize(5);
有人会问了,是不是每次创建一个statement都要设置一下fetchsize大小,答案是没必要,还有一种简单的方法。
就是通过jdbc url参数附带,跟useCursorFetch一样的方式,如:
jdbc:mysql://192.168.5.240:3306/turbo_2?useCursorFetch=true&defaultFetchSize=4
为什么这样可以?参考源码:这个是mysql驱动的源码
在StatementImpl的构造方法中,有如下代码:从连接connection中获取defaultFetchSize的大小,作为初始的fetchsize,如果没有设置,默认的是0
int defaultFetchSize = this.connection.getDefaultFetchSize();
if (defaultFetchSize != 0) {
setFetchSize(defaultFetchSize);
}
3.关于jdbc底层参数
jdbc的层级关系如下:
Connection--connection参数,这些参数可以通过jdbc url参数设置 比如:defaultFetchSize maxRows
--Statement或Preparestatment-- statement参数,可以得到以后通过set设置,也可以通过上级connection给出一个默认的,这样所有的statement都将使用默认的参数值。像fetchSize maxRows
4.fetchSize与maxRows的区别
fetchSize 是一次与数据库通信获取到的数据量,如果数据量大,会分多次通信获取数据,直到结果集中的数据全部获取完。这个参数主要是用来限制一次获取到的数据量,保证无论结果集有多大,都不会撑爆jvm内存
maxRows 是这个结果集只允许返回的数据量,比如maxRows 设置了2,但是结果集应该有5条,那么无论查询多少次,都只返回2条,后面的3条永远获取不到。这个参数用处不是很大。