我爱普洱茶 发表于 2024-7-13 04:47:26

JDBC查询大数据时怎么防止内存溢出-流式查询

1.媒介

在利用 JDBC 查询大数据时,由于 JDBC 默认将整个结果集加载到内存中,当查询结果集过大时,很容易导致 JVM 内存溢出的问题。
办理办法通常是利用分页查询,但是分页查询越往后要遍历的行数越多,服从越低。除非能够添加索引条件,但这又进步了业务逻辑的复杂度。
2.流式查询介绍

JDBC的流式查询就是在利用ResultSet对象获取查询结果集的时间,不是把结果集一次性全部加载到内存中,而是分批次读取数据。
在jdbc客户端和mysql服务端创建tcp连接后,mysql以包的情势返回数据。在查询大数据的情况下,需要分多个包发送给客户端,而流式查询就是一次读取一个包的数据(通常情况下如此),以是查询的数据巨细与MySQL一次发送的包巨细痛痒相关。可以通过MySQL的配置max_allowed_packet设置包巨细上限。
3.利用流式查询

java需要引入jdbc的依靠。
3.1不开启流式查询的内存占用情况

测试代码如下:
private static void testFetch() throws SQLException {
    Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "zhuzi", "123456");
    Statement s = c.createStatement();
    //查询1000w条数据
    ResultSet rs = s.executeQuery("select * from gg limit 10000000");
    while (rs.next()) {
                //执行处理数据的逻辑
    }
    //休眠100s,方便查看内存情况
    try {
      Thread.sleep(100000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    rs.close();
    s.close();
    c.close();
}
利用jconsole工具查察内存利用情况,如下图所示。
可以看到,大约占用了1.5GB的内存,并且内存曲线很安稳,这阐明数据是一次性全部加载到内存中的。
https://img-blog.csdnimg.cn/direct/1306e6e258e048b4bea9cfe5fe3c574f.png#pic_center
3.2开启流式查询的内存占用情况

测试代码如下:
private static void testFetch() throws SQLException {
    Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "zhuzi", "123456");
    //必须设置为TYPE_FORWARD_ONLY和CONCUR_READ_ONLY 当然默认也是这两个值,可以不写
    Statement s = c.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    //必须设置为Integer.MIN_VALUE,其他值都不会生效
    s.setFetchSize(Integer.MIN_VALUE);//-2147483648
    ResultSet rs = s.executeQuery("select * from gg limit 10000000");
    while (rs.next()){
                //执行处理数据的逻辑
    }
    try {
      Thread.sleep(100000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    rs.close();
    s.close();
    c.close();
}
内存占用情况如下:
可以看到,仅占用了几十MB内存,内存占用极大的降低了,并且内存利用在慢慢增加,这是由于数据是一批一批不断加载进来的,但前面的数据还没来得及清算。但现实上我们用完一批数据那么这批数据占用的内存就能够开释掉了。
https://img-blog.csdnimg.cn/direct/cb71e2a05475440fb4d7532f1c504ba8.png#pic_center
4.开启流式查询的留意点

前面的测试代码中提到了,在调用Statement对象的setFetchSize方法时,通报的参数必须为-2147483648,否则不会开启流式查询。
StatementImpl类源码界说如下:
protected boolean createStreamingResultSet() {
    return this.query.getResultType() == Type.FORWARD_ONLY && this.resultSetConcurrency == 1007 && this.query.getResultFetchSize() == -2147483648;
}
该方法用于判断是否开启流式查询,可以看到,它要求ResultType为FORWARD_ONLY,ResultSetConcurrency为CONCUR_READ_ONLY,以及ResultFetchSize为-2147483648
在ResultSet类中这些变量的界说如下:
//查询结果通过next方法只能向后遍历,不能使用previous方法往前遍历
//开启该选项后调用previous方法回报错:
//Operation not allowed for a result set of type ResultSet.TYPE_FORWARD_ONLY.
int TYPE_FORWARD_ONLY = 1003;

//查询结果可前后遍历,数据库数据改变不会影响结果集
int TYPE_SCROLL_INSENSITIVE = 1004;

//查询结果可前后遍历,数据库数据改变会影响结果集(测试了,好像没用,不知道怎么做)
int TYPE_SCROLL_SENSITIVE = 1005;

//结果集只能读
int CONCUR_READ_ONLY = 1007;

//结果集可以修改,并且对结果集的修改能够同步到数据库
int CONCUR_UPDATABLE = 1008;
参考博客:
Mysql中JDBC的三种查询(平凡、流式、游标)详解
精确利用MySQL JDBC setFetchSize()方法办理JDBC处理大结果集

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: JDBC查询大数据时怎么防止内存溢出-流式查询