为什么采用流式处理
生产环境中常常遇到千万级甚至亿级的大表,对于这种表,一次查询哪怕查询的数据经过了索引,往往也需要一秒甚至多秒的时间,如果类似的查询次数可能要多达上百万的情况下,光对于这一张表的就可能需要消耗数十个小时,跑的慢不可怕,要是业务逻辑错了、代码有BUG、服务器挂了…要是重试几次,别说实现需求了,人都凉了。
快,是现代社会的最基础的要求,起码程序发布一次需要跑上一天或数天是完全难以接受的。
数据库流式读正是解决这一问题的法宝,虽然采取流式读不得不对全表进行数据处理,却省略了因为反复的查询带来的额外开销,反倒极大提高了程序处理效率。
(实测在对一些数据量约5000万左右的表,根据业务走索引查询10万次需消耗7、8个小时,但是采用流式读全表并筛选相关数据的方式可以把这个流程降到30min以内,可以说在速度上有质的突破)
流式处理方式
话不多说,直接上代码:
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int fetchSize = 5000;
String sql=""; // 这里是你需要查询的数据的sql
try {
conn = jdbcTemplate.getDataSource().getConnection();
conn.setAutoCommit(false);
pstmt = (PreparedStatement) conn
.prepareStatement(
sql
,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
ResultSet.CLOSE_CURSORS_AT_COMMIT);
pstmt.setFetchSize(fetchSize);
pstmt.setFetchDirection(ResultSet.FETCH_FORWARD);
rs = pstmt.executeQuery();
while (rs.next()) {
// 这里进行业务逻辑的处理
}
rs.close();
} catch (Exception e) {
try {
if (conn != null)
conn.rollback();
} catch (SQLException e1) {
logger.error(" ",e);
}
logger.error(" ",e);
} finally {
try {
pstmt.close();
// 这边使用release的方式来释放数据库连接,避免因为使用共享的连接池而获取到已关闭的连接
// conn.close();
DataSourceUtils.releaseConnection(conn,jdbcTemplate.getDataSource());
} catch (Exception e) {
logger.error(" ",e);
}
}
}
需要注意的点
- fetchSize:流式处理时每次从数据库拉取的数据量
- 数据库连接: 需要在处理完毕后进行关闭或者进行释放(推荐的方式)
- 一定要避免在流式读的过程中再嵌套的去进行其他耗时的处理,导致流式读的快照失效,整个流程直接中止