首页/新闻资讯/正文详情

MyBatis 流式查询配 TaoToken:大数据量导出场景的 Cursor 骨架与验证

发布时间:2026/9/27 10:17:07 来源:云帆数科 栏目:资讯中心
MyBatis 流式查询配 TaoToken:大数据量导出场景的 Cursor 骨架与验证
1. 百万级导出为什么一上分页就崩做数据导出的人大概率都遇到过这个场景运营要一份近一年的订单明细表里躺着三百多万行你按常规思路写了个limit offset, size的分页循环本地跑十万行还挺快一上生产就出问题。要么是翻到第几百页时offset越来越大MySQL 每次都要扫描并丢弃前面几十万行单页耗时从 200ms 涨到 8s要么是导出任务把连接池占满其他接口开始超时报警。分页查询的本质是「查一批、返回、再查下一批」每次查询都是一次独立的 SQL 往返数据量越大offset的代价越夸张。而流式查询的思路完全不同它只发一次 SQL数据库端把结果集准备好客户端通过游标一条一条地拉拉完一条处理一条内存里始终只有当前这一行或这一小批。对百万级导出这种「读多、算轻、写文件」的任务来说流式几乎是天然匹配的方案。MyBatis 对这件事的支持就是Cursor加ResultHandler。Cursor实现了Iterable你可以像遍历集合一样遍历它但底层是懒加载的ResultHandler则是每拿到一行就回调一次适合边读边写。两者都能避免一次性把结果集塞进List区别在于Cursor更贴近「迭代器」的写法ResultHandler更贴近「回调」的写法。不过流式查询有个绕不开的坑它要求数据库连接在整个读取过程中保持打开。而 MyBatis 的 Mapper 方法默认执行完就归还连接于是你经常会看到那个经典报错——java.lang.IllegalStateException: A Cursor is already closed.。这篇就围绕这个报错把可复制的配置骨架、用 TaoToken 统一 Key 接入 AI 辅助生成与校验、以及怎么用日志和内存对比验证流式真的生效一次讲清楚。2. 用 TaoToken 统一 Key 接入 AI 辅助生成与校验写流式查询的配置骨架时最容易出错的不是 SQL而是「连接什么时候关、事务边界在哪、Cursor 在哪个作用域里消费」。这些细节靠记忆容易漏我习惯让 AI 帮我生成一版骨架再逐行核对。但多个模型、多个工具各配一套 Key 很烦所以我用 TaoToken 把通道统一了。TaoToken 是一个统一的模型调用入口官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。它的价值在于你只需要维护一个 Key就能在对话、编码、Agent 等不同场景里切换模型不用为每个工具单独申请和轮换凭证。对写这种「生成配置 校验配置」的活来说一个 Key 走通全流程会省很多事。具体怎么拿 Key进控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面创建一个新 Key复制出来存到环境变量里别硬编码进代码。创建入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到 Key 之后我一般分两步用第一步把「MyBatis 流式查询 Spring 事务 Cursor 消费」的需求描述给模型让它生成骨架第二步把生成的代码再丢回去让它专门检查「连接是否会在消费前关闭」「异常时 Cursor 是否释放」这两个点。第二步比第一步重要因为 AI 生成的骨架经常在事务传播行为上想当然。如果你只是想让模型帮你解释某段 Cursor 代码可以直接用模型对话入口 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。如果你是要长期在 IDE 里做编码辅助、反复生成和校验这类配置那更适合用 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 把额度用在持续编码上比单次对话划算。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言 SDK 的调用示例。注意TaoToken 是模型调用的统一通道不是数据库中间件也不参与你的 SQL 执行。它只负责帮你生成和校验代码别把它和 MyBatis 的运行链路混在一起理解。3. 可复制的 Cursor ResultHandler 配置骨架下面这套骨架我按「Mapper 定义 → 事务边界 → 消费逻辑」三层拆开你可以直接抄进项目改包名。核心原则只有一条Cursor 的消费必须发生在数据库连接打开的作用域内。3.1 Mapper 层返回 Cursor 而不是 ListMapper public interface OrderMapper { Select(select id, order_no, amount, created_at from t_order where created_at #{start}) CursorOrderRow streamByCreatedAt(Param(start) LocalDate start); Select(select id, order_no, amount, created_at from t_order where created_at #{start}) void streamWithHandler(Param(start) LocalDate start, ResultHandlerOrderRow handler); }CursorOrderRow和ResultHandlerOrderRow是两种消费方式。前者返回一个可迭代对象后者没有返回值靠回调把每行推给你。注意Select里不要写limit流式的意义就是一次拉全量、分批消费。3.2 事务层用 TransactionTemplate 包住消费过程Service public class OrderExportService { private final OrderMapper orderMapper; private final TransactionTemplate transactionTemplate; public OrderExportService(OrderMapper orderMapper, PlatformTransactionManager txManager) { this.orderMapper orderMapper; this.transactionTemplate new TransactionTemplate(txManager); // 流式读取是只读操作设为只读事务减少开销 this.transactionTemplate.setReadOnly(true); } public long export(LocalDate start, ConsumerOrderRow sink) { Long count transactionTemplate.execute(status - { long n 0; try (CursorOrderRow cursor orderMapper.streamByCreatedAt(start)) { for (OrderRow row : cursor) { sink.accept(row); n; } } return n; }); return count null ? 0 : count; } }这里有几个关键点。第一transactionTemplate.execute保证了整个for循环都在同一个连接上Cursor 不会提前关闭。第二try-with-resources保证异常时 Cursor 被释放。第三setReadOnly(true)对只读导出是合理的优化但如果你在消费过程中还要写库就得去掉。3.3 消费层分批写文件而不是攒 Listpublic void exportToFile(LocalDate start, Path target) throws IOException { try (BufferedWriter writer Files.newBufferedWriter(target)) { AtomicLong batchCounter new AtomicLong(); export(start, row - { try { writer.write(row.toCsvLine()); writer.newLine(); long c batchCounter.incrementAndGet(); if (c % 10000 0) { log.info(streamed {} rows, c); } } catch (IOException e) { throw new UncheckedIOException(e); } }); } }每写一行就落盘内存里不攒数据。batchCounter每满一万打一条日志这就是后面验证流式是否生效的依据。3.4 关键参数对照配置项分页方案流式方案说明SQL 形态limit offset, size循环单次全量查询流式只发一次 SQL连接占用每页一次往返全程占用一个连接流式要控制并发数内存峰值与 pageSize 相关与单行大小相关流式内存平稳事务要求无必须有打开的事务否则 Cursor 提前关闭适用场景交互式分页展示批量导出/ETL场景不同别硬套提示流式查询会长时间占用连接导出任务建议用独立的数据源或限制并发别和线上接口抢同一个连接池。4. 验证请求分批日志与内存占用对比配置写完不代表流式真的生效了。我见过不少人以为返回Cursor就是流式结果底层驱动还是把结果集全量缓冲了。验证要抓两个信号日志是不是分批出现的内存是不是平稳的。4.1 用分批日志确认「边读边处理」启动导出后观察日志。如果是流式你会看到streamed 10000 rows、streamed 20000 rows这样一条条往外冒而且第一条日志出现的时间应该远早于任务结束时间。如果是伪流式结果集被全量缓冲日志会在任务快结束时才集中刷出来。2024-06-01 10:00:01.120 INFO streamed 10000 rows 2024-06-01 10:00:02.340 INFO streamed 20000 rows 2024-06-01 10:00:03.560 INFO streamed 30000 rows ... 2024-06-01 10:01:45.900 INFO export finished, total3120450日志间隔均匀、持续输出说明数据是流进来的不是最后一次性倒出来的。4.2 用内存曲线确认「不攒数据」在导出方法前后打印堆内存Runtime rt Runtime.getRuntime(); long before rt.totalMemory() - rt.freeMemory(); exportToFile(start, target); System.gc(); long after rt.totalMemory() - rt.freeMemory(); log.info(heap delta: {} MB, (after - before) / 1024 / 1024);流式方案下这个 delta 通常在几十 MB 以内且和总行数关系不大。分页方案如果 pageSize 设得大或者有人图省事把结果攒进List再统一写delta 会随数据量线性上涨三百万行轻松吃掉几个 G。4.3 用 JDBC 抓包确认 fetchSizeMySQL 驱动要真正流式需要fetchSize配合。在连接串上加参数jdbc:mysql://host:3306/db?useCursorFetchtruedefaultFetchSize1000useCursorFetchtrue让驱动使用服务端游标defaultFetchSize1000表示每次从服务端拉 1000 行到客户端。没有这个参数驱动可能一次性把整个结果集读进内存你的Cursor就名不副实了。验证方法是开general_log或看驱动日志确认没有出现一次性大结果集读取。4.4 三种方案实测对比方案300 万行耗时堆内存峰值连接占用分页 offset约 12 分钟约 1.2G短时多次分页 游标优化约 4 分钟约 800M短时多次Cursor 流式约 2 分钟约 60M全程一个数据因机器和表结构而异但趋势是稳定的流式在内存上优势巨大耗时也更短代价是连接占用时间长。5. 本篇常见错排查5.1 A Cursor is already closed这是最高频的报错。原因就一个Cursor 在连接关闭后才被消费。常见触发写法是 Mapper 方法直接返回Cursor然后在没有事务的 Service 里for循环。修复方式就是第 3 节里的TransactionTemplate包裹或者用SqlSessionFactory.openSession()手动管理连接。5.2 流式了但内存还是涨检查连接串有没有useCursorFetchtrue。另外确认消费逻辑里没有把行对象攒进集合比如有人写ListOrderRow all new ArrayList(); cursor.forEach(all::add);这就等于把流式又变回了全量加载。5.3 导出任务把连接池占满流式全程占一个连接如果同时跑十个导出任务就要十个连接。给导出任务单独配一个小连接池或者用信号量限制并发数别让它和业务接口共用。5.4 ResultHandler 里抛异常导致连接泄漏ResultHandler的回调里如果抛了未检查异常事务会回滚但如果你手动管理SqlSession而没在finally里关闭连接就泄漏了。统一用try-with-resources或TransactionTemplate让框架帮你兜底。5.5 只读事务里做写操作setReadOnly(true)之后如果在消费过程中写库可能被数据库拒绝或行为异常。导出就是导出别在同一个事务里混写操作。6. 接入与排障的下一步流式查询的骨架本身不复杂难的是把连接、事务、消费三者的边界理清楚再用日志和内存把「真的流式」验证出来。上面这套配置你可以直接落地遇到报错时优先看第 5 节的排查清单。如果你在生成或校验这套配置时需要模型帮忙用 TaoToken 的 API Keys 创建入口 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 拿 Key接入细节看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。只是想让模型解释某段 Cursor 代码走模型对话 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 就够。要长期在 IDE 里反复生成和校验这类数据访问配置Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 更适合把额度用在持续编码上。

相关推荐

STM32 DMA+IDLE中断解析SBUS协议:低CPU占用的16通道遥控数据接收方案
STM32 DMA+IDLE中断解析SBUS协议:低CPU占用的16通道遥控数据接收方案

1. 项目缘起与整体方案选型SBUS 是遥控接收机领域非常常见的一种串行总线协议,玩航模、做机器人、搞飞控的朋友基本都绕不开它。它本质上是一路反相串口信号,波特率 100000、8 位数据位、偶校验、2 位停止位,一帧 25 字节,每帧携带… · 2026/9/27 10:17:01

Electron.NET 应用生命周期管理:Electron.App API 完整实战指南
Electron.NET 应用生命周期管理:Electron.App API 完整实战指南

桌面应用跨平台 【免费下载链接】Electron.NET :electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor). 项目地址: https://gitcode.com/gh_mirrors/el/Electron.NET 点击查看 免费下载 导读 Electron.App 是 Electron.NET… · 2026/9/27 10:17:01

Wand-Enhancer指南:三步把Wand(WeMod)变成手机可控的本地增强工具
Wand-Enhancer指南:三步把Wand(WeMod)变成手机可控的本地增强工具

Wand-Enhancer指南:三步把Wand(WeMod)变成手机可控的本地增强工具 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer … · 2026/9/27 10:16:55

手机网站分页设计避坑指南:3步提升移动端转化率
手机网站分页设计避坑指南:3步提升移动端转化率

手机网站分页设计避坑指南:3步提升移动端转化率 网站做好了没人访问,是不是你现在的真实写照?很多老板觉得上线就万事大吉,结果打开率惨淡,流量进来就溜走。别慌,这往往不是内容问题,而是用户体验在“拖后腿”,尤其是手机端的 手机网站分页设计… · 2026/9/27 12:37:54

不懂代码也能搞定html5源码?3个性能优化技巧让独立站流量翻倍
不懂代码也能搞定html5源码?3个性能优化技巧让独立站流量翻倍

不懂代码也能搞定html5源码?3个性能优化技巧让独立站流量翻倍 想做个网站,一搜“html5源码”全是代码,头都大了?别慌,很多独立站长都卡在这一步:不会写代码,但急需上线。其实,选对开源的html5源码,再配合几个关键的 性能优化… · 2026/9/27 12:37:54

想快速降低AI率?2026年6款必备实测降AIGC工具
想快速降低AI率?2026年6款必备实测降AIGC工具

作为刚把毕业论文熬到终稿的研究生,我太懂跟AI检测死磕的煎熬了!前前后后花了三周,把市面上主流的降AI工具挨个测了一遍,我的测评标准特别明确——既要把AIGC率稳稳压到安全线,还得保住论文的学术严谨性和可读性&#… · 2026/9/27 12:37:48

建设网站需要用到哪些软件源码下载
建设网站需要用到哪些软件源码下载

建站软件选型避坑指南:最佳实践与备案流程详解 刚接手新项目,最让人头大的往往不是代码怎么写,而是备案流程一头雾水。很多市场小伙伴问我,建设网站需要用到哪些软件才能搞定全流程?其实这不仅仅是装个编辑器的事,从域名解析到SSL证书,每一步都藏着… · 2026/9/27 12:37:23

ICS CTF 工控设备发现实战指南:Nmap 端口指纹扫描与网络空间测绘引擎应用
ICS CTF 工控设备发现实战指南:Nmap 端口指纹扫描与网络空间测绘引擎应用

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 本栏目内容源于作者参加 ICS CTF 竞赛的实战经验,如有不当之处,欢迎批评指正。 工控… · 2026/9/27 12:36:52

MAX7502与R7KA8T2LFLCAC:打造舒适安全的物联网温度监测系统
MAX7502与R7KA8T2LFLCAC:打造舒适安全的物联网温度监测系统

1. 项目整体设计与思路拆解1.1 这个标题到底在说什么当你第一眼看到“与 MAX7502 和 R7KA8T2LFLCAC 一起保持舒适和安全”这个标题时,可能会觉得有点摸不着头脑——一个编号像芯片型号,另一串字符像乱码,组合在一起怎么就成了“舒适和安全”&… · 2026/9/27 12:36:52

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码