MySQL环境下,mybatis使用Cursor:从配置到验证的完整实践

发布时间:2026/9/28 4:12:14

MySQL环境下,mybatis使用Cursor:从配置到验证的完整实践 1. 为什么大结果集查询会把内存打爆先说结论MySQL 环境下用 MyBatis 做普通查询默认会把整个结果集一次性读进 JVM 内存几百万行数据直接 OOM。Cursor游标就是解决这个问题的——它让 JDBC 逐批从服务端拉数据而不是一口气全拿回来。我在一个订单对账项目里踩过这个坑单表 800 万行用ListOrder selectAll()查出来做遍历堆内存 4G 直接撑爆GC 日志全是 Full GC。后来改成 Cursor 流式读取内存曲线立刻平了稳定在几百 MB。Cursor 适合谁适合需要遍历大结果集做批处理、导出、对账、数据迁移的场景。不适合的场景是你只需要查几条数据或者需要把结果集反复随机访问——那种情况用普通 List 更省事。这里有个关键前提MySQL 的流式查询不是随便设个fetchSize就生效的。它有一套特定的触发条件配错了就是伪 Cursor数据照样全量加载。下面我把配置骨架、验证方法、常见坑一次讲清楚。2. TaoToken 前置把模型对话和编码辅助接进来在动手改 MyBatis 配置之前我习惯先把调试辅助工具准备好。排查 Cursor 问题时经常需要问模型这段 JDBC 源码为什么这样判断或者让模型帮我 review Mapper 写法。TaoToken 在这里的作用是提供一个统一的模型调用入口省去到处找 Key 的麻烦。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content如果你只是想快速验证某个模型对 MyBatis 源码的理解直接用模型对话页最方便 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你在做长期的编码工作比如反复调试 Cursor 配置、让 Agent 帮你改 Mapper那 Coding Plan 更合适 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite需要自己管理调用凭证的话去控制台和 API Keys 页面 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI 地址统一是https://taotoken.net/api接入文档在这里 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite用 Claude Code 做编码辅助的Anthropic 兼容入口 https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite这些工具不是必须的但排查 Cursor 这种涉及 JDBC 底层源码的问题时有个能随时问的模型确实省时间。3. 可复制的 MyBatis Cursor 配置骨架3.1 环境与依赖版本先确认版本Cursor 的行为在不同版本间有差异组件版本说明JDK8Cursor 接口在 JDK8 可用MyBatis3.5.2低版本 Cursor 支持不完整mysql-connector-java8.0.228.x 系列流式判断逻辑一致MySQL Server5.7 / 8.0都支持流式结果集Maven 依赖dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.22/version /dependency3.2 Mapper XML 写法核心是fetchSize-2147483648也就是Integer.MIN_VALUE。这个值不是随便写的后面会解释原因。?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.demo.mapper.MenuMapper select idcursor resultTypecom.demo.entity.Menu fetchSize-2147483648 select menu_id as menuId, menu_name as menuName from t_menu where menu_id lt; #{menuId} /select /mapper注意fetchSize写在select标签上不是写在全局 settings 里。全局 settings 里没有这个配置项别找错地方。3.3 Mapper 接口返回类型必须是org.apache.ibatis.cursor.Cursor不能是Listpackage com.demo.mapper; import com.demo.entity.Menu; import org.apache.ibatis.cursor.Cursor; public interface MenuMapper { CursorMenu cursor(Integer menuId); }3.4 Service 层SqlSession 类型是关键这一步最容易出错。必须用ExecutorType.REUSE打开 SqlSession用 BATCH 或 SIMPLE 都会导致结果集被提前关闭。package com.demo.service; import com.demo.entity.Menu; import com.demo.mapper.MenuMapper; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Iterator; Service public class MenuService { Autowired private SqlSessionFactory sessionFactory; public void streamMenu() { SqlSession sqlSession sessionFactory.openSession(ExecutorType.REUSE); try { MenuMapper mapper sqlSession.getMapper(MenuMapper.class); CursorMenu cursor mapper.cursor(1000000); IteratorMenu iterator cursor.iterator(); while (iterator.hasNext()) { Menu menu iterator.next(); System.out.println(menu.getMenuId() - menu.getMenuName()); } } finally { sqlSession.close(); } } }如果你用 Spring 的Transactional注意事务边界。Cursor 必须在同一个 SqlSession 生命周期内消费完跨事务会报 Cursor is closed。4. 验证请求与成功结果4.1 为什么必须是 Integer.MIN_VALUEMySQL Connector/J 判断是否启用流式结果集的逻辑在com.mysql.cj.jdbc.StatementImpl#createStreamingResultSetprotected boolean createStreamingResultSet() { return ((this.query.getResultType() Type.FORWARD_ONLY) (this.resultSetConcurrency java.sql.ResultSet.CONCUR_READ_ONLY) (this.query.getResultFetchSize() Integer.MIN_VALUE)); }三个条件同时满足才走流式结果集类型是FORWARD_ONLY、并发模式是CONCUR_READ_ONLY、fetchSize 等于Integer.MIN_VALUE。所以-2147483648不是魔法数字是 MySQL 驱动约定的开关值。4.2 验证游标是否真的生效光看代码跑通不够要确认数据是分批拉的。两个验证方法方法一看内存曲线。用jconsole或jvisualvm挂到进程上跑一个百万行查询。如果内存平稳说明流式生效如果内存阶梯式上涨到峰值说明是伪 Cursor。方法二加日志看拉取节奏。在遍历循环里每 10000 条打一次时间戳int count 0; while (iterator.hasNext()) { Menu menu iterator.next(); if (count % 10000 0) { System.out.println(已处理 count 条, 时间: System.currentTimeMillis()); } }真 Cursor 的时间戳是均匀递增的伪 Cursor 会在开头卡很久全量加载然后瞬间打印完。4.3 成功结果对照跑通后你应该看到控制台逐条打印菜单数据不是一次性刷屏内存占用稳定不随数据量线性增长处理 100 万行耗时比普通查询略长因为网络往返多了但内存安全5. 本篇常见错误排查5.1 报 Cursor is closed原因SqlSession 被提前关闭或者用了ExecutorType.BATCH/SIMPLE。BatchExecutor#doQueryCursor里查询完就关了结果集SimpleExecutor同理。只有ReuseExecutor不关闭能持续从服务端取数据。改成ExecutorType.REUSE即可。5.2 内存还是爆了检查三处fetchSize是否写成-2147483648写成1000无效resultType是否误写成List是否在遍历前把 Cursor 转成了 List。5.3 遍历时报连接超时流式查询会长时间占用连接MySQL 的wait_timeout默认 8 小时一般够用。但如果处理逻辑很慢建议调大net_write_timeout和net_read_timeout。5.4 Spring 事务里 Cursor 失效Transactional默认用 SIMPLE executor。要么手动开 REUSE 的 SqlSession要么在事务方法里用SqlSessionTemplate指定 executorType。注意Cursor 消费完必须关闭 SqlSession否则连接泄漏。用 try-finally 包住。6. 接入与排障资源Cursor 配置本身不复杂难的是排查为什么没生效。遇到 JDBC 源码层面的疑问或者需要模型帮你 review Mapper 和 Service 写法可以用这些入口API Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite接入文档含各语言 SDK 示例https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite快速验证模型对源码的理解用模型对话https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite长期做编码调试和 Agent 辅助用 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI 基础地址https://taotoken.net/api最后补一个实战技巧生产环境用 Cursor 时给遍历循环加个批量提交的节奏控制比如每 5000 条做一次业务侧落库或缓存写入避免长时间持有连接。另外 Cursor 不支持RowBounds分页要分页就自己在 SQL 里加limit别指望 RowBounds 和 Cursor 混用。
延伸阅读

更多相关文章

2026/9/28 4:07:14

3个维度对比评测网站网页策略,避开90%新手坑

3个维度对比评测网站网页策略,避开90%新手坑 域名买好了,服务器也租了,结果网站打不开? 别慌,这是我在过去十年建站生涯里见得最多的场景。 很多前端初学者刚接触 网站网页策略…

2026/9/28 4:07:14

Cadence培训怎么选?从需求自检到机构筛选的完整指南

/* 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 5:02:18

JSP家教系统实战:可部署的JavaWeb毕设原型

简介:本资源是一个基于JSP技术开发的“大学生兼职家教网”完整Web项目,面向Java Web初学者与课程设计实践者,聚焦真实场景下的信息匹配与用户管理需求,助力掌握B/S架构开发全流程。压缩包共79个文件,含19个JSP页面&…

2026/9/28 5:02:18

商用翻译机落地指南:从参数采购到高频使用

很多单位在采购商用翻译设备时,往往陷入一个误区:盯着参数表里的准确率、语种数量猛看,觉得只要技术指标够硬,买回去就能立刻提升国际化接待效率。然而现实情况却是,不少高昂的设备拆封使用几次后,就被遗忘…

2026/9/28 5:02:18

景区评论情感分析:对抗+注意力+Bi-LSTM模型设计与落地

简介:本资源是一套面向本科毕业设计与深度学习初学者的景区评论情感分析实战项目,聚焦旅游领域文本情感判别任务,融合对抗训练与注意力机制提升Bi-LSTM模型鲁棒性与可解释性。压缩包共18个文件,含4个Jupyter Notebook(…

2026/9/28 5:02:18

3步拆解廊坊百度关键词推广从零搭建避坑全案

3步拆解廊坊百度关键词推广从零搭建避坑全案 别再对着那些千篇一律的模板网站发呆了,那种丑到让人想关浏览器的页面,根本撑不起你的业务形象。很多廊坊做建材、机械或者物流的朋友,花了几千块买个现成模板,结果上线后流量惨淡,百度搜不到,客户更看不上…

2026/9/28 5:02:18

S32K3xx HSE安全启动实战:SMR分区、密钥烧录与多核协同

/* 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 4:57:18

SpringBoot性能优化的7个实战技巧

SpringBoot用起来爽,但默认配置是为开发便利设计的,不是为高并发。上线后QPS上不去、响应慢,往往不是代码逻辑问题,而是配置没调。下面7个实战技巧,每个都经过生产验证,照做就能见效。一、调优内嵌Tomcat线…

2026/9/28 3:03:23

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

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

2026/9/27 0:00:45

如何划分训练/验证集: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/27 0:00:45

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

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

2026/9/28 0:02:03

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑 改个需求建站公司拖一周,后台改个文案还得再交一笔“技术维护费”。这种憋屈事儿,做外贸的朋友太熟悉了。很多老板在找广州外贸网站建设推广服务商时,光盯着首页好不好看,却忽略了从零搭建一个能…

2026/9/28 0:02:04

搞懂百度竞价推广价格,网站性能优化别掉链子

搞懂百度竞价推广价格,网站性能优化别掉链子 网站突然打不开,浏览器弹出红色警告“此网站存在安全风险”,后台一看全是乱码代码和奇怪的跳转链接。这种网站被黑挂马的绝望感,很多刚转行做网站的朋友都经历过,尤其是那些为了省几百块钱服务器费用的新手。…

2026/9/25 20:55:38

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/28 1:59:25

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

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

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

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

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