启用jdbc流式查询需同时设置三个参数:resultset.type_forward_only、resultset.concur_read_only、setfetchsize(integer.min_value),并配置jdbc url添加usecursorfetch=true和defaultfetchsize=1000,代码须严格按顺序执行executequery()后遍历next(),且用try-with-resources确保资源释放。

核心办法是启用 JDBC 流式查询,让数据一行一行“流”进来,而不是一次性全加载进内存。关键不在 SQL 写法,而在连接、语句和结果集的底层配置。
必须设置的三个参数
缺一不可,否则流式不起作用:
- Statement 类型必须是 TYPE_FORWARD_ONLY:只允许从头到尾单向读取,不能倒序或随机跳转
- 并发模式必须是 CONCUR_READ_ONLY:禁止在 ResultSet 上做更新操作,这是流式前提
- fetchSize 必须设为 Integer.MIN_VALUE(即 -2147483648):这是 JDBC 规范定义的“流式开关”,设成其他任何正数(比如 1000)都只是普通分批拉取,不是真流式
MySQL 连接串要加关键参数
光代码设对还不够,驱动层得配合。MySQL 5.0+ 推荐在 JDBC URL 中显式开启游标模式:
- useCursorFetch=true:启用服务端游标,让 MySQL 分包返回数据
- defaultFetchSize=1000:配合 useCursorFetch,控制每次网络包里传多少行(注意:这和 Statement.setFetchSize(Integer.MIN_VALUE) 不冲突,前者是服务端行为,后者是客户端开关)
- 示例完整 URL:jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8&useCursorFetch=true&defaultFetchSize=1000
代码写法要严格遵循顺序
流式 ResultSet 对资源管理更敏感,稍错一步就报错或卡死:
- 必须先执行 executeQuery(),再调用 next() 开始读,不能跳过首行
- 必须把所有行读完(或显式 close()),才能复用同一 Connection 执行下一条 SQL
- 不要在循环中嵌套新查询,也不要跨线程共享 ResultSet
- 推荐用 try-with-resources 自动关闭,但需确保 ResultSet 关闭前已遍历完毕
别踩这些典型坑
很多 OOM 并不是没开流式,而是被细节绊倒:
- 用了 MyBatis?默认不支持流式。需改用 SqlSession#selectStream() 或原生 JDBC
- 用 Druid 连接池?确认其未自动重写 fetchSize 或禁用流式(某些老版本会覆盖 Integer.MIN_VALUE)
- 字段含大文本(如 TEXT、BLOB)?即使开了流式,单行本身也可能超内存,建议 select 时排除非必要大字段
- MySQL 配置 max_allowed_packet 太小?会导致单包截断,流式中断,建议调至 64M 以上
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











