前言:

最近做数据同步,需要从PostgreSql获取数据,发现一旦数据比较多,那么读取的速度非常慢,并且内存占用特别多&GC不掉。

代码样例:

为了方便讲解,下面写了事例代码,从b2c_order获取数据,这个数据表6G左右。

package com.synchro;

import java.sql.*;

/**
* Created by qiu.li on 2015/10/16.
*/
public class Test { public static void main(String[] args) {
Connection conn = null;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection("jdbc:postgresql://***.qunar.com:5432/database", "username", "password");
String sql = "select * from mirror.b2c_order";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
int i = 0;
while (rs.next()) {
i++;
if (i % 100 == 0) {
System.out.println(i);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

现象:

在Idea执行代码,发现卡死,并且占用大量的内存

解决方案:

然后我决定开始逐步调试,跟踪代码:

第一步、我发现是在执行executeQuery方法的时候卡住的

第二步、是在执行AbstractJdbc2Statement.executeWithFlags方法卡住的

第三步、继续跟踪,并在网络上查看可能引起的原因是和设置fetchSize参数相关,所以我设置了fetchSize,奇葩的是没有生效

第四步、sendQuery,sendOneQuery方法,在这里发现了问题,好在代码不太多,我就都贴出来了:

        boolean usePortal = (flags & 8) != 0 && !noResults && !noMeta && fetchSize > 0 && !describeOnly;
boolean oneShot = (flags & 1) != 0 && !usePortal;
int rows;
if(noResults) {
rows = 1;
} else if(!usePortal) {
rows = maxRows;
} else if(maxRows != 0 && fetchSize > maxRows) {
rows = maxRows;
} else {
rows = fetchSize;
}

可见是usePortal是true,那么fetchSize才会生效。

boolean usePortal = (flags & 8) != 0 && !noResults && !noMeta && fetchSize > 0 && !describeOnly;

那么咱们逐一看一下这些条件:

  • !noResults表示这个SQL不需要返回任何结果,这个肯定等于true,因为所有的select都会要求返回结果
  • !noMeta表示这个SQL不需要返回元数据,这个肯定等于true,因为select都要求返回元数据,供后续的resultSet.get使用
  • !fetchSize大于0,这个不说了,自然是true
  • !describeOnly,这个只有在desc table这样的语句的时候,才会是false,对于select,也是true

那么,试下的唯一的可能导致usePortal为true的原因就是 flags & 8这个值是true。。(我想说这种写法很别致,tmd,设置flags的时候肯定是flags=flag|8,后来发现新的驱动修改了这种写法)

继续往上翻,看看什么时候才会执行flags = flags | 8 这个代码了,因为只有这个代码被执行过,才会导致上面这个条件为true

        if(this.fetchSize > 0 && !this.wantsScrollableResultSet() && !this.connection.getAutoCommit() && !this.wantsHoldableResultSet()) {
flags |= 8;
}

其中:wantsHoldableResultSet()代码直接返回的false,所以,不考虑这个。

那么,wantsScrollableResultSet()返回false,并且connection.getAutoCommit()返回false,才会导致fetchSize生效。wantsScrollableResultSet()这个方法的代码为:

protected boolean wantsScrollableResultSet() {
return resultsettype != 1003; //老代码,看到这里我真想死,1003是啥?好在偶然的机会看见了新的Postgresql驱动,使用ResultSet.TYPE_FORWARD_ONLY表示1003
}

至此,问题终于被定位:

1、如果connection不是自动提交事务的,那么,fetchSize将生效(非默认)

2、如果statement是TYPE_FORWARD_ONLY的,那么,fetchSize也将生效(默认)

结论

如果想fetchSize生效,必须保证connection是autocommit = false的,并且,statement为1003(forward_only)的:

conn.setAutoCommit(false);
final Statement statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.FETCH_FORWARD);

另外,不带参数的conn.createStatement(),其默认就是TYPE_FORWARD_ONLY。所以,一般情况下,如果想fetchsize生效,只须设置autocommit为flase,也就是需要手工去管理事务。默认的源代码如下:

    public Statement createStatement() throws SQLException {
return this.createStatement(1003, 1007); //有兴趣的同学可以继续跟踪看看,1003就是resultsettype
}

代码:

那么修改代码如下:

package com.synchro;

import java.sql.*;

/**
* Created by qiu.li on 2015/10/16.
*/
public class Test { public static void main(String[] args) {
Connection conn = null;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection("jdbc:postgresql://***.qunar.com:5432/datasource", "username", "password");
conn.setAutoCommit(false); //并不是所有数据库都适用,比如hive就不支持,orcle不需要
String sql = "select * from mirror.b2c_order";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setFetchSize(1000); //每次获取1万条记录
//ps.setMaxRows(1000);
ResultSet rs = ps.executeQuery();
int i = 0;
while (rs.next()) {
i++;
if (i % 100 == 0) {
System.out.println(i);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

这次再一次执行,发现根本不卡。

感悟:类似这种问题都的慢慢跟踪代码,更重要的是身边需要有同事可以相互讨论,形成氛围,因为这个过程十分乏味,自己很难坚持下来。

参考文献

https://jdbc.postgresql.org/documentation/head/query.html

http://m.blog.csdn.net/blog/itjin45/42004447#

http://blog.csdn.net/hantiannan/article/details/4509167

Jdbc如何从PostgreSql读取海量数据?PostgreSql源代码分析纪录的更多相关文章

  1. PostgreSQL入门,PostgreSQL和mysql

    PostgreSQL被誉为“世界上功能最强大的开源数据库”,是以加州大学伯克利分校计算机系开发的POSTGRES 4.2为基础的对象关系型数据库管理系统. PostgreSQL支持大部分 SQL标准并 ...

  2. PostgreSQL CPU满(100%)性能分析及优化(转)

    PostgreSQL CPU满(100%)性能分析及优化 转自:https://help.aliyun.com/knowledge_detail/43562.html    在数据库运维当中,一个DB ...

  3. Mac安装postgresql和卸载PostgreSQL

    1.homebrew安装 brew install postgresql 2.初始化 initdb /usr/local/var/postgres 3.创建数据库及查看数据库 (1)先创建db. cr ...

  4. PostgreSQL——服务器配置_{postgresql.conf}

    一.设置参数 所有参数名称都是不区分大小写的 值为字符串时,需要单引号 值为数值时不需要单引号,但带单位时,需要单引号 配置文件(如:postgresql.conf.postgresql.auto.c ...

  5. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  6. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  7. 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析

    刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部分,本篇文章总结第三部分,也是最后一部分.这部分主要讲解核心技术的 ...

  8. Hadoop源代码分析(完整版)

    Hadoop源代码分析(一) 关键字: 分布式云计算 Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http:// ...

  9. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

随机推荐

  1. C++ 升级到 Vs2013后编译设置

    编译 EasyDarwin 时,Vs2008的C++升级到 Vs2013时报错: 1. 找不到 windows.h 项目->属性->配置属性->C/C++->所有选项: 附加包 ...

  2. MVC中用Jpaginate分页 So easy!(兼容ie家族)

    看过几款分页插件,觉得Jpaginate比较简约,样式也比较容易的定制,而且体验也比较好,支持鼠标滑动效果.先上效果图: 整个过程很简单,只需要3步 一.引入相关样式和脚本: 1.MVC4中,用了Bu ...

  3. Window程序的安装与部署

    步骤: 1.新建项目—选择安装与部署—安装项目或使用安装向导,再这里我用的是安装向导 2.点击确定—下一步 3.点击下一步,选择主输出 4.点击下一步,添加文件 5.点击完成 设置: 右击安装项目 出 ...

  4. Atitit.Hibernate中Criteria 使用总结and 关联查询 and 按照子对象查询 o9o

    Atitit.Hibernate中Criteria 使用总结and 关联查询 and 按照子对象查询 o9o 1. Criteria,,Criterion ,, 1 <2. 主要的对象黑头配置磊 ...

  5. Atitit.实现反向代理(1)----url rewrite 配置and内容改写 and -绝对路径链接改写 java php

    Atitit.实现反向代理(1)----url rewrite  配置and内容改写 and -绝对路径链接改写 java php 1. 代理的实现:::普通代理and反向代理?? 1 2. url  ...

  6. paip.java 调用c++ dll so总结

    paip.java 调用c++ dll so总结 ///////JNA (这个ms sun 的) 我目前正做着一个相关的项目,说白了JNA就是JNI的替代品,以前用JNI需要编译一层中间库,现在JNA ...

  7. (二)关于ajax那些事

    哈哈,今天突然兴起,想了想自己对ajax的了解,来这里叙述下.心情好,嘿嘿嘿 ajax是一种创建交互式网页应用的网页开发技术.就是在不重新加载页面的情况下,更新部分网页. ajax原理:ajax就是相 ...

  8. Cause for NullPointerException android.support.v7.widget.RecyclerView.onMeasure

    because you have not set LinearLayoutManager to RecyclerView. for example: mRecyclerView = (Recycler ...

  9. OpenBTS的安装(转)

    OpenBTS source code可以在这里下载:http://sourceforge.net/projects/openbts/ OpenBTS入门的各种问题可以在这里找到答案:http://g ...

  10. IOS6屏幕旋转详解(自动旋转、手动旋转、兼容IOS6之前系统)

    转自 http://blog.csdn.net/zzfsuiye/article/details/8251060 概述: 在iOS6之前的版本中,通常使用 shouldAutorotateToInte ...