MyBatis游标Cursor在Oracle数据库上的测试:TaoToken统一Key下fetchSize调优与配置骨架

发布时间:2026/9/29 6:34:19

MyBatis游标Cursor在Oracle数据库上的测试:TaoToken统一Key下fetchSize调优与配置骨架 1. 为什么 Oracle 大结果集一拉就爆内存先说结论在 Oracle 上做百万级数据导出或批处理MyBatis Cursor 合理的 fetchSize是绕不开的组合。如果你只是把SELECT结果一次性ListT返回JVM 堆会随着行数线性上涨99 万行数据跑十几分钟都出不来中途还可能 OOM。我试过在 2G 初始堆、4G 最大堆的环境下直接查全表内存缓慢爬升12 分钟没跑完最后只能手动终止。问题的根子在于 JDBC 默认行为。Oracle JDBC 驱动在不显式设置fetchSize时默认值是10。也就是说驱动每次只从服务端取 10 行到客户端网络往返次数极多吞吐极低而如果你用普通List查询MyBatis 又会把驱动返回的所有行全部堆在内存里行数一多就撑爆堆。这两个问题方向相反却经常同时出现。MyBatis 的CursorT解决的是「客户端不堆积」的问题它返回一个可迭代的游标你iterator一行处理一行处理完的对象可以被 GC 回收。但它不解决「网络往返太频繁」的问题那要靠fetchSize。所以真正稳定的方案是Cursor负责内存fetchSize负责吞吐两者配合才能既省内存又跑得快。这篇面向需要稳定拉取大结果集的 Java 后端给出可复制的 MyBatis 配置片段、Oracle JDBC 的 fetchSize 设置、Cursor 查询示例以及一套最小验证步骤。同时说明怎么用 TaoToken 的统一 Key/API 通道接入 AI 工具辅助排查配置和报错。适合正在做数据迁移、报表导出、对账批处理的同学。2. TaoToken 前置统一 Key 接入 AI 辅助排查调优过程中最烦的不是写代码而是遇到报错不知道从哪查。比如ORA-01000: maximum open cursors exceeded、fetchSize设了没生效、Cursor 迭代到一半连接被回收这些问题的排查往往要翻文档、搜 issue。我的做法是接一个统一的 AI 通道来辅助定位TaoToken 就是干这个的它把多个模型的调用收敛到一个 Key、一个 API 地址上不用为每个工具单独配一套凭证。对 Java 后端来说它的价值在于你在 IDE 里、在命令行里、在写排障脚本时都能用同一个 Key 去问「这个 Oracle 报错通常什么原因」「MyBatis Cursor 为什么 fetchSize 不生效」不用来回切换账号。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。具体到本篇场景我建议这样分流排查接入类问题Key 怎么配、请求 401、模型名写错→ 去 API Keys 页面拿 Key再看接入文档验证某个模型能不能用、回答质量如何 → 用模型对话页面直接试长期做编码辅助、Agent 批处理 → 看 Coding Plan。拿 Key 的入口在控制台的 API Keys 里https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。想先验证模型效果就去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你用 Claude Code 这类编码工具Anthropic 兼容入口在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。注意TaoToken 在这里的角色是「AI 工具的统一接入通道」用来辅助你查报错、生成配置骨架不是数据库中间件也不参与你的 JDBC 连接。数据库连接始终是你自己的 Oracle 实例。3. 可复制配置MyBatis Oracle fetchSize 骨架这一节是核心直接给能跑的配置。分三块Mapper 接口、XML 映射、以及 Oracle JDBC 的 fetchSize 设置。3.1 Mapper 接口返回 Cursor关键点返回类型写CursorT而不是ListT。Cursor继承自Iterable可以配合 try-with-resources 自动关闭。import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; Mapper public interface BigDataMapper { // fetchSize 通过 Options 指定Oracle 下建议 1000~10000 Options(fetchSize 10000, resultSetType org.apache.ibatis.session.ResultSetType.FORWARD_ONLY) Select(SELECT id, order_no, amount, create_time FROM t_order WHERE create_time #{startTime}) CursorOrderRow streamOrders(org.apache.ibatis.annotations.Param(startTime) java.util.Date startTime); }resultSetType必须是FORWARD_ONLY否则 Oracle 可能把结果集缓存到临时表反而更慢。fetchSize这里给 10000是实测下来吞吐和内存比较平衡的值后面会讲为什么。3.2 XML 映射写法等价配置如果你习惯 XML等价写法如下。注意resultSetTypeFORWARD_ONLY和fetchSize10000都要写。select idstreamOrders resultTypecom.example.OrderRow fetchSize10000 resultSetTypeFORWARD_ONLY SELECT id, order_no, amount, create_time FROM t_order WHERE create_time gt; #{startTime} /select3.3 Oracle JDBC 连接参数光在 MyBatis 层设fetchSize有时不够因为连接池或驱动可能覆盖它。建议在 JDBC URL 或连接属性里显式声明。Oracle 的defaultRowPrefetch就是驱动级默认 fetchSize默认 10。# application.properties spring.datasource.urljdbc:oracle:thin://127.0.0.1:1521/ORCLPDB1 spring.datasource.usernameapp_user spring.datasource.passwordyour_password # 驱动级默认预取行数和 MyBatis 的 fetchSize 配合 spring.datasource.hikari.data-source-properties.oracle.jdbc.defaultRowPrefetch10000 # 关闭自动提交游标读取期间保持事务 spring.datasource.hikari.auto-commitfalse如果你用 Druid把data-source-properties换成connection-properties写法类似。核心是让驱动默认预取和 MyBatis 的fetchSize一致避免一个设 10000 一个还是 10。3.4 消费端一行一行处理拿到Cursor后用迭代器消费处理完的对象及时释放引用。下面这段是标准骨架。import org.apache.ibatis.cursor.Cursor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; Service public class OrderExportService { private final BigDataMapper bigDataMapper; public OrderExportService(BigDataMapper bigDataMapper) { this.bigDataMapper bigDataMapper; } Transactional public long export(java.util.Date startTime) { long count 0; // try-with-resources 保证 Cursor 关闭释放数据库游标 try (CursorOrderRow cursor bigDataMapper.streamOrders(startTime)) { for (OrderRow row : cursor) { // 这里做你的业务写文件、发 MQ、批量入库 process(row); count; } } catch (Exception e) { throw new RuntimeException(游标读取失败, e); } return count; } private void process(OrderRow row) { // 示例只做轻量处理避免在循环里再查库 } }注意Cursor必须在事务内使用。MyBatis 的Cursor依赖SqlSession保持打开如果方法没有TransactionalSqlSession可能在迭代前就被关闭报ExecutorException: A Cursor is already closed。4. 验证请求与预期结果配置写完怎么确认fetchSize真的生效了给你一套最小验证步骤不用百万数据也能看出差别。4.1 造一张测试表先用一张几十万行的表验证。Oracle 里可以这样快速造数CREATE TABLE t_order ( id NUMBER PRIMARY KEY, order_no VARCHAR2(64), amount NUMBER(12,2), create_time DATE ); -- 插入 50 万行测试数据 BEGIN FOR i IN 1..500000 LOOP INSERT INTO t_order VALUES ( i, NO || TO_CHAR(i, FM000000000), DBMS_RANDOM.VALUE(1, 10000), SYSDATE - DBMS_RANDOM.VALUE(0, 365) ); IF MOD(i, 10000) 0 THEN COMMIT; END IF; END LOOP; COMMIT; END; /4.2 打开 JDBC 日志确认预取在application.properties里打开 MyBatis 和 JDBC 的调试日志logging.level.com.example.mapperDEBUG logging.level.org.mybatisDEBUG跑一次export观察日志里 SQL 执行后是否有fetchSize相关输出。更直接的办法是抓 Oracle 的v$session或v$sql看FETCH_COUNT是否接近你设的值。如果还是 10说明配置被覆盖了回到 3.3 检查连接属性。4.3 对比内存与耗时用jconsole或jstat -gc pid 1000观察堆变化。预期结果配置fetchSize内存峰值耗时GC 次数普通 List 全查默认 10持续增长易 OOM12 分钟未完成多Cursor 默认10低但稳定5 分钟未完成少Cursor 调优10000约 164MB约 49s36Cursor 极限省内存10000 50MB 堆约 42MB约 42s158从这张表能看出关键规律fetchSize太小10、100时网络往返和 GC 会吃掉大量时间吞吐只有 40%~76%fetchSize调到 10000 后耗时降到 40 多秒内存也控制在百兆级。不是 fetchSize 越小越省内存就越好太小反而因为频繁 GC 拖慢整体。4.4 用 TaoToken 辅助看报错如果验证时遇到ORA-01000打开游标数超限可以把报错贴给 AI 工具问原因。通过 TaoToken 的统一通道用同一个 Key 就能问curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-3-5-sonnet, messages: [ {role: user, content: Oracle 报 ORA-01000 maximum open cursors exceededMyBatis Cursor 场景下常见原因和排查方向} ] }把$TAOTOKEN_API_KEY换成你在控制台拿到的 Key。返回结果里通常会提示检查open_cursors参数、确认 Cursor 是否及时关闭、以及事务边界。这类问题用 AI 辅助定位比翻文档快很多。5. 本篇常见错排查这一节把踩过的坑集中列一下都是 Oracle MyBatis Cursor 场景下高频出现的。5.1 fetchSize 设了不生效最常见的原因是连接池或驱动覆盖。Oracle 驱动的defaultRowPrefetch默认 10如果 MyBatis 的fetchSize没传到PreparedStatement就会用驱动的默认值。排查顺序先看 MyBatis 日志里PreparedStatement的fetchSize再看连接属性里defaultRowPrefetch是否被设成别的值。两者要一致。5.2 Cursor 报 already closedCursor依赖SqlSession存活。如果方法没有Transactional或者你在Transactional方法外迭代CursorSqlSession提前关闭就会报这个错。解决把迭代逻辑放在事务方法内部或者用SqlSessionTemplate手动管理。5.3 ORA-01000 打开游标超限每个未关闭的Cursor占一个数据库游标。如果循环里反复开Cursor不关或者try-with-resources没写对游标数会累积。检查v$open_cursor确认Cursor都在 try-with-resources 里关闭。必要时调大open_cursors但根治还是靠及时关闭。5.4 内存限制太小导致 GC 爆炸把堆压到 20MB 跑 10000 的 fetchSize会看到 GC 次数飙到几千次GC 耗时占一半以上吞吐掉到 40%。这说明省内存和吞吐要平衡。实测 50MB 堆 fetchSize 10000 是比较好的点内存 42MBGC 只有 158 次耗时 42 秒。不要盲目把堆压到极限。5.5 MySQL 的 fetchSize 特殊写法顺带提一句MySQL 下要开启流式传输fetchSize得设成Integer.MIN_VALUE这是 MySQL 驱动的特殊标记。Oracle、PostgreSQL 等大多数数据库都是正常用法通过控制fetchSize大小来减少内存占用和数据库交互次数。别把 MySQL 的写法套到 Oracle 上会不生效。6. 接入与排障用统一 Key 收口 AI 辅助回到工具链。调优和排障过程中AI 辅助能省不少时间但前提是接入别太碎。TaoToken 的价值就是把 Key 和 API 地址统一你在写排障脚本、配 IDE 插件、跑 Agent 时用同一套凭证。具体分流建议排障、接入类问题Key 配置、401、模型名、请求格式→ 先去 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 拿 Key再看 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档验证模型回答质量、试不同模型 → https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 长期做编码辅助、Agent 批处理 → https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 用 Claude Code 类工具 → https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。API 基址统一用 https://taotoken.net/api 这个不加 UTM。官网总入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后给一个实用技巧把常用的排障 prompt 存成脚本比如「Oracle 报错 上下文」的模板用 curl 调 TaoToken 的 API 直接问比每次手动开网页快。Cursor 调优这类问题AI 给的排查方向通常能帮你快速缩小范围但最终验证还是要回到jstat、v$session这些真实指标上。配置骨架照第 3 节抄验证照第 4 节跑基本能覆盖大部分 Oracle 大结果集场景。
延伸阅读

更多相关文章

2026/9/29 6:29:19

2026年CTF趋势与备考攻略:AI Agent、MCP与缝合题型全解析

2025年我打了差不多四十场CTF,线上为主,中间也冲过几次线下赛。把这一年的题放在一起复盘,最明显的变化不是分数高低,而是出题逻辑变了:AI开始真正参与答题,MCP协议被摆上台面,以前那种单考一个…

2026/9/29 7:34:22

代码审计中常见的伪漏洞,SRC 提交前快速排查

代码审计中常见的伪漏洞,SRC 提交前快速排查 摘要 很多白帽在源码审计挖到疑似漏洞,兴冲冲提交到 SRC,结果被厂商判定为伪漏洞、无法复现、误报,浪费大量时间。伪漏洞指代码片段看起来存在安全风险,但受业务逻辑、参…

2026/9/29 7:34:22

华为手机备份实战指南:ADB、USB调试与微信QQ数据导出

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 7:34:22

FFT频谱分析实操:幅值换算、能量守恒与处理增益详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 7:34:22

x86硬件级进程切换:TSS、TR与GDT底层机制解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 7:34:22

Win11下Java环境安装指南:JDK选择与环境变量配置详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 3:03:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/26 19:58:38

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑