
1. 从一次 1000 条插入只快 8% 说起MyBatis-Plus 批量插入性能优化到底卡在哪先说结论MyBatis-Plus 的insertBatch在 1000 条这个量级和 for 循环单条insert的耗时差距经常只有个位数百分比。我第一次压测时也懵了明明叫批量插入怎么跟一条条插差不多。翻源码才发现ServiceImpl.insertBatch内部就是拿SqlSession循环insert每batchSize条flushStatements一次。它省的是 SqlSession 反复开关的开销并没有把多条 INSERT 合并成一条 SQL。所以真正决定批量插入性能的是三个东西JDBC URL 有没有开rewriteBatchedStatementstrue、批次大小设多少、以及 SQL 本身是不是多值 INSERT。前两个是配置问题第三个是 SQL 生成问题。而 SQL 生成这件事恰好可以用 TaoToken 的统一 Key 通道让多个模型分别产出候选 SQL 和配置再交叉验证省掉自己反复查文档试错的时间。这篇面向的是已经在用 Spring Boot MyBatis-Plus、手里有真实写入压力的后端同学。你会拿到一份可直接复制的批量插入配置、一段多模型生成 SQL 的调用示例、以及一套能跑出对比数据的验证步骤。核心检索词就是 MyBatis-Plus 批量插入性能优化围绕它把配置、SQL、验证三块讲透。先明确一个认知批量插入的瓶颈通常不在 MyBatis-Plus 这一层而在 JDBC 驱动和数据库服务端。MySQL 默认会把executeBatch拆成一条条发送只有开启rewriteBatchedStatements后驱动才会把多条 INSERT 重写成INSERT INTO t (...) VALUES (...),(...),(...)这种多值形式。不开这个参数你换什么 ORM 都白搭。这也是为什么很多人觉得insertBatch是「搞了个寂寞」——参数没配对。下面按「问题定位 → 统一 Key 准备 → 可复制配置 → 验证请求 → 报错排查 → 落地建议」的顺序展开。每一步都给命令和结果说明你可以边看边在本地跑。2. TaoToken 统一 Key 准备多模型生成 MyBatis-Plus 批量插入 SQL 的接入方式为什么要用多模型来生成 SQL因为批量插入的写法在不同数据库、不同 MyBatis-Plus 版本下差异不小。MySQL 的多值 INSERT、Oracle 的INSERT ALL、PostgreSQL 的ON CONFLICT还有 MyBatis-Plus 3.5.4 之后新增的insertBatchSomeColumn每个模型给的答案可能不一样。让两三个模型各写一版再拿真实执行计划去验证比只信一个来源稳。TaoToken 在这里的作用是提供一个统一的 API 通道和一把 Key你不用为每个模型单独申请账号、记不同的 Base URL。它的接口兼容 OpenAI 风格的/v1/chat/completions所以任何支持自定义 Base URL 的客户端都能接。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 根地址是 https://taotoken.net/api 。接入前你需要准备三件套这三件套在任何客户端里都要填全缺一个就连不上配置项值说明Base URLhttps://taotoken.net/api注意结尾不带/v1具体路径由客户端拼接API Key在控制台创建形如sk-开头的一串字符Model ID如gpt-4o、claude-3-5-sonnet等以控制台模型列表为准创建 Key 的入口在控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。点进去新建一个复制出来存到环境变量里别硬编码进代码。我习惯这样export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Claude Code 这类命令行工具它需要单独配置 Anthropic 兼容端点参考文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。配置思路和上面一致Base URL 填 TaoToken 的地址Key 填刚创建的Model ID 填你要用的模型。三件套齐全请求才能落到正确的模型上。这里提醒一句TaoToken 是模型调用通道不是数据库代理也不是编辑器替代品。它的职责是让你用一把 Key 调多个模型来生成和校验 SQL数据库连接、MyBatis-Plus 配置这些还是在你自己的项目里。别把两者混在一起理解。准备好 Key 之后先做一次最小连通性测试确认通道没问题再进入 SQL 生成环节。测试命令curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [{role: user, content: 回复 ok}] }返回里能看到choices[0].message.content就说明通道通了。如果返回 401先检查 Key 有没有复制完整、有没有多余空格。这一步过了后面的多模型调用才有意义。3. 可复制配置MyBatis-Plus 批量插入的 JDBC 参数与多模型调用片段这一节给两份可直接粘贴的东西一份是让批量插入真正生效的 JDBC 与 MyBatis-Plus 配置一份是调用多模型生成 SQL 的请求体。两份都验证过路径和参数保持原样即可。先看 JDBC 配置。关键参数是rewriteBatchedStatementstrueMySQL 驱动必须开。完整 URL 示例spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghairewriteBatchedStatementstrueallowMultiQueriestrue username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 20 minimum-idle: 5rewriteBatchedStatementstrue是核心allowMultiQueriestrue在你手动拼多值 SQL 时会用到。MyBatis-Plus 侧如果你用ServiceImpl自带的insertBatch批次大小通过重载方法传// 每 500 条 flush 一次避免一次性攒太多导致内存和网络压力 boolean ok workIService.insertBatch(entityList, 500);但前面说过这个自带方法本质还是循环。要拿到真正的多值 INSERT得自己写 Mapper 方法。下面这段 XML 是 MySQL 下的多值插入配合rewriteBatchedStatements使用效果最好insert idinsertBatchCustom parameterTypejava.util.List INSERT INTO cmp_work (work_id, work_name, cre_tm) VALUES foreach collectionlist itemitem separator, (#{item.workID}, #{item.workName}, #{item.creTm}) /foreach /insert对应 Mapper 接口public interface WorkMapper extends BaseMapperCmpWork { int insertBatchCustom(Param(list) ListCmpWork list); }注意Param(list)不能省否则 XML 里的collectionlist找不到参数。这是很常见的坑报错信息通常是Parameter list not found。接下来是调用多模型生成 SQL 的请求体。你可以把表结构、MyBatis-Plus 版本、目标数据库一起丢给模型让它产出候选 SQL。请求示例curl -s 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: system, content: 你是 MyBatis-Plus 与 MySQL 专家只输出可执行的 XML 片段和配置说明。}, {role: user, content: 表 cmp_work 字段work_id varchar, work_name varchar, cre_tm datetime。MyBatis-Plus 3.5.3MySQL 8。请给出批量插入的 Mapper XML 和 JDBC 参数要求开启 rewriteBatchedStatements。} ], temperature: 0.2 }把model换成另一个模型再发一次就得到第二版候选。两版对比着看差异点往往就是容易踩坑的地方。比如有的模型会漏掉Param有的会建议用insertBatchSomeColumn这些差异正好帮你判断哪个方案更适合当前版本。如果你要长期做这类 SQL 生成和校验可以考虑 Coding Plan它更适合高频、多轮的编码辅助场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。偶尔用一次的话按量调用就够了。4. 验证请求与成功结果跑出 MyBatis-Plus 批量插入的真实性能对比配置写完必须用数据说话。这一节给一套可复现的验证流程跑完你能拿到「单条 insert vs 自带 insertBatch vs 多值 insertBatchCustom」三组耗时。第一步准备测试数据。10000 条字段简单ListCmpWork list new ArrayList(10000); for (int i 0; i 10000; i) { CmpWork w new CmpWork(); w.setWorkID(testBatch i); w.setWorkName(workNametestBatch i); w.setCreTm(new Date()); list.add(w); }第二步分别计时。用System.currentTimeMillis()包住调用即可long t1 System.currentTimeMillis(); for (CmpWork w : list) { workMapper.insert(w); } long t2 System.currentTimeMillis(); System.out.println(单条 insert 耗时: (t2 - t1) ms); long t3 System.currentTimeMillis(); workIService.insertBatch(list, 500); long t4 System.currentTimeMillis(); System.out.println(自带 insertBatch 耗时: (t4 - t3) ms); long t5 System.currentTimeMillis(); workMapper.insertBatchCustom(list); long t6 System.currentTimeMillis(); System.out.println(多值 insertBatchCustom 耗时: (t6 - t5) ms);第三步看结果。在本地 MySQL 8、10000 条数据的实测下典型结果是单条 insert 约 4200ms自带 insertBatch 约 3800ms多值 insertBatchCustom 约 900ms。差距主要来自多值 SQL 把 10000 次网络往返压成了几十次。注意这个数字随机器和网络变化重点看相对关系。如果你开了rewriteBatchedStatementstrue但自带insertBatch还是慢大概率是批次大小没调。把batchSize从 500 调到 1000 或 2000 再试观察耗时曲线。批次太大反而会因为单次 SQL 过长触发max_allowed_packet限制报错信息是Packet for query is too large。这时候要么调大数据库参数要么把批次降下来。验证时还要看数据库端的表现。开一个会话执行SHOW GLOBAL STATUS LIKE Com_insert;跑之前记一次值跑完再记一次差值应该接近你插入的总条数。如果差值远小于总条数说明有部分请求被合并或失败回滚了需要检查事务边界。多模型生成的 SQL 也要在这一步验证。把模型给的 XML 贴进项目跑同一份数据对比耗时和结果条数。如果某个模型给的 SQL 执行报错把报错原文连同表结构再发回模型让它修正。这个来回过程用统一 Key 就很顺不用切换账号。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth 报错对照这一节把接入和运行中最容易撞上的报错列出来对照着改。401 Unauthorized。出现在调用 TaoToken 接口时说明 Key 不对。检查三点Key 是否复制完整、请求头是不是Authorization: Bearer sk-xxx、环境变量有没有被 shell 转义。用echo $TAOTOKEN_API_KEY确认值非空。如果 Key 刚创建等几秒再试偶尔有同步延迟。local proxy failed。这个报错通常出现在客户端配置了本地代理但代理没起来或者 Base URL 写成了带/v1的完整路径导致拼接重复。TaoToken 的 Base URL 填https://taotoken.net/api客户端一般会自己补/v1/chat/completions。如果你手动填了https://taotoken.net/api/v1就会变成/api/v1/v1/...报 404 或代理失败。改回不带/v1的根地址即可。reading choices 相关报错。典型信息是Cannot read properties of undefined (reading choices)意思是响应体里没有choices字段。原因通常是请求根本没成功返回的是错误 JSON但客户端没检查状态码就直接取choices。排查方法先用 curl 发一次同样的请求看原始返回。如果返回{error: {...}}按 error 里的 message 处理。常见的是模型 ID 写错比如把claude-3-5-sonnet写成了不存在的名字。OAuth 相关报错。如果你用 Claude Code 或类似工具它可能走 OAuth 流程而不是纯 API Key。报错信息里带OAuth或token exchange failed时检查工具版本和配置格式。Claude Code 的配置需要 Base URL、Key、Model ID 三件套齐全缺 Model ID 会走到默认模型上导致鉴权失败。参考文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 按里面的字段名逐项核对。MyBatis-Plus 侧报错。Parameter list not found是Param缺失Packet for query is too large是批次太大Error: entityList must not be empty是传了空列表加个判空即可。还有一个隐蔽的insertBatch在 Oracle 下不可用源码注释里写了「该方法不适合 Oracle」Oracle 要用INSERT ALL或存储过程。排查顺序建议先确认 TaoToken 通道通curl 测试再确认 SQL 能单独执行数据库客户端跑一遍最后才看 ORM 层。分层定位比一上来就翻源码快得多。6. 落地建议把 MyBatis-Plus 批量插入性能优化固化进项目最后给几条实操建议都是踩过坑之后总结的。第一JDBC URL 里的rewriteBatchedStatementstrue写进配置模板别靠记忆。新项目初始化时就把这个参数带上省得后面排查半天。第二批次大小做成可配置项默认 500压测时再调。不同数据库、不同网络环境下最优值不一样硬编码不灵活。第三多值 INSERT 的 XML 用foreach拼注意separator,和Param(list)配对。如果字段多考虑用insertBatchSomeColumn配合自定义 SQL 注入器减少手写 XML 的维护成本。第四多模型生成的 SQL 一定要过一遍真实执行。模型给的语法可能对但索引、字段类型、字符集这些细节它看不到。把EXPLAIN结果也丢回模型让它帮你分析执行计划比只看 SQL 文本有用。第五长期做这类编码辅助用 Coding Plan 比按量调用更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果只是偶尔生成几段 SQL按量调用配合模型对话页面就够了https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。需要新建 Key 或查看用量去控制台https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。接入细节以文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。把上面这套跑一遍你手里就有一份能直接用的批量插入配置和一组真实对比数据。剩下的就是按自己项目的表结构和数据量微调批次大小然后固化进代码模板。