1. Oracle 大批量 DML 为什么必须按数据量分批次提交
先说结论:Oracle 里一次性插入几百万行,最容易踩的两个坑不是语法写错,而是 undo 表空间被撑爆和锁持有时间过长。前者报 ORA-30036(unable to extend segment in undo tablespace),后者让其他会话长时间等锁,业务侧表现为「卡死」。分批次提交(batch commit)就是在这两个坑之间找平衡点:每处理 N 行提交一次,把单个事务的 undo 占用和锁范围压到可控区间。
我试过在一个 800 万行的迁移任务里直接一条INSERT INTO ... SELECT,跑了 40 分钟,undo 表空间从 2G 涨到 12G 还没结束,最后被 DBA 叫停。改成每 5000 行提交一次后,undo 峰值稳定在 1.5G 以内,整体耗时反而缩短到 25 分钟,因为回滚段不用反复扩展。
这里要区分两个概念。批量 DML指的是单条 SQL 影响大量行,比如INSERT INTO t2 SELECT * FROM t1;分批提交指的是把大事务拆成多个小事务,每个小事务处理固定行数后COMMIT。前者是 SQL 层面的写法,后者是事务粒度控制。两者可以叠加:用游标逐行取数、按阈值提交,就是最经典的 PL/SQL 分批模板。
适合谁看:做数据迁移、历史表归档、大表数据清洗的 Oracle 开发或 DBA;正在被 ORA-01555(snapshot too old)或 ORA-30036 折磨的人;以及想把批量脚本通过统一 API 通道调度起来、不想在每台机器上散落一堆连接串的团队。
核心检索词先摆出来:Oracle 按数据量分批次提交,本质是「用可控的事务粒度换取 undo 与锁的稳定」。批次大小不是拍脑袋定的,它和你的 undo 表空间大小、UNDO_RETENTION、单行数据宽度、是否有触发器都相关。下面从接入准备讲到可复制模板,再到验证动作和报错排查,一步步来。
需要说明的是,分批提交不是越多越好。提交太频繁(比如每 100 行一次)会带来两个副作用:一是 LGWR 写 redo 的次数暴增,二是如果游标是基于一致性读的长查询,频繁提交会让 undo 被覆盖,反而更容易触发 ORA-01555。所以批次大小要结合场景调,后文会给参数对照表。
2. TaoToken 统一 Key 前置准备:把脚本调度通道先打通
在写 PL/SQL 之前,先把「谁来触发这个批量脚本」这件事解决掉。很多团队的做法是每台应用服务器配一份数据库连接串,密码散落在各个配置文件里,改一次密码要动十几个地方。用 TaoToken 的统一 Key 可以把模型调用、脚本调度、API 通道收敛到一套凭证上,批量 DML 脚本通过 API 触发时只认一个 Key。
TaoToken 是什么:它是一个统一的大模型与 API 接入网关,把不同模型的调用收敛到同一个 Base URL 和同一套 API Key 下。能做什么:你可以用它在脚本里调用模型做数据校验、生成 SQL、或者把批量任务的执行结果回传做分析。适合谁:需要把数据库运维脚本和 AI 能力串起来、又不想管理多套密钥的开发和运维。
前置准备分三步。第一步,拿到统一 Key。访问 https://taotoken.net/api-keys 创建,注意这个页面是控制台里的密钥管理入口,创建后复制保存,页面关闭后不再完整显示。第二步,确认 Base URL。API 通道统一用 https://taotoken.net/api,不要带任何额外参数。第三步,选模型 ID。做 SQL 生成或数据校验,选一个代码能力强的模型即可,模型 ID 在模型对话页面能看到。
这里有个容易忽略的点:TaoToken 的 Key 是给 API 通道用的,不是数据库账号密码。数据库连接仍然走你原来的 tnsnames 或 EZConnect,两者不要混。批量脚本的调度链路是「调度器 → TaoToken API → 你的执行脚本 → Oracle」,TaoToken 负责的是凭证统一和调用入口,不碰你的数据库连接。
如果你用的是 Claude Code 这类编码工具来维护批量脚本,可以在工具里配置 Base URL 和 Key,让它帮你生成和审查 PL/SQL。配置入口在 https://taotoken.net/doc 有说明。长期跑批量任务的团队,可以考虑 Coding Plan,把脚本生成、审查、调度串成一条流水线,入口在 https://taotoken.net/coding-plan。
准备阶段还要确认一件事:你的 Oracle 用户有没有CREATE PROCEDURE权限,以及DBMS_LOCK的执行权限(模板里会用到 sleep 做测试)。没有的话让 DBA 授权,或者把 sleep 那行注释掉。权限不够时报 ORA-01031,别以为是脚本写错了。
3. 可复制的分批提交 PL/SQL 模板与批次参数配置
这一节给可直接粘贴的模板。先看核心结构:用显式游标取源表数据,用%ROWCOUNT计数,每满一个批次阈值就COMMIT,循环结束后再补一次COMMIT兜底。批次大小用常量C_BATCH_SIZE控制,方便改。
DECLARE C_BATCH_SIZE CONSTANT PLS_INTEGER := 5000; -- 每 5000 行提交一次 TYPE CUR IS REF CURSOR; MY_CUR CUR; COL_NUM SCOTT.EMP_TEST%ROWTYPE; NUM PLS_INTEGER := 0; BEGIN OPEN MY_CUR FOR SELECT * FROM SCOTT.EMP_TEST; LOOP FETCH MY_CUR INTO COL_NUM; EXIT WHEN MY_CUR%NOTFOUND; NUM := MY_CUR%ROWCOUNT; INSERT INTO SCOTT.EMP_2019 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) VALUES (COL_NUM.EMPNO, COL_NUM.ENAME, COL_NUM.JOB, COL_NUM.MGR, COL_NUM.HIREDATE, COL_NUM.SAL, COL_NUM.COMM, COL_NUM.DEPTNO); IF MOD(NUM, C_BATCH_SIZE) = 0 THEN COMMIT; DBMS_OUTPUT.PUT_LINE('committed at ' || NUM || ' rows, ' || TO_CHAR(SYSDATE,'HH24:MI:SS')); END IF; END LOOP; CLOSE MY_CUR; COMMIT; -- 兜底提交剩余不足一批的数据 DBMS_OUTPUT.PUT_LINE('total rows: ' || NUM); END; /这段和原始 excerpt 的差别在于:把批次大小提成常量、去掉了测试用的 sleep、加了时间戳输出方便观察节奏。%ROWCOUNT在游标里是累计取到的行数,所以MOD(NUM, 5000) = 0就是每 5000 行触发一次提交。
批次大小怎么定?给一张对照表,按 undo 表空间大小和单行宽度估算:
| undo 表空间 | 单行约宽 | 建议批次 | 说明 |
|---|---|---|---|
| 2G | 200B | 2000–5000 | 小表空间保守取值 |
| 4G | 200B | 5000–10000 | 常见生产配置 |
| 8G+ | 500B | 10000–20000 | 宽表可适当放大 |
| 任意 | 含 LOB | 500–1000 | LOB 的 undo 开销大 |
参数配置还有两个关键项。UNDO_RETENTION决定 undo 数据保留多久,设得太小,长查询容易撞 ORA-01555;设得太大,undo 表空间压力大。生产环境建议开自动 undo 管理(UNDO_MANAGEMENT=AUTO),让 Oracle 自己调。查询当前设置:
SHOW PARAMETER undo_management; SHOW PARAMETER undo_retention; SHOW PARAMETER undo_tablespace;如果你要把这个脚本通过 TaoToken 的 API 通道调度,可以在脚本外层包一个 shell,用 curl 触发。配置片段如下,注意 Base URL 和 Key 的写法:
{ "base_url": "https://taotoken.net/api", "api_key": "你的统一Key", "model_id": "你的模型ID", "task": "oracle_batch_dml", "batch_size": 5000 }这个 JSON 是给调度器读的配置,不是 Oracle 的配置。把 batch_size 和 PL/SQL 里的常量保持一致,避免两处对不上。模型 ID 填你在模型对话页面选定的那个,做 SQL 审查时用它来检查脚本有没有漏掉兜底 COMMIT。
再强调一次三件套:Base URL 是 https://taotoken.net/api,Key 从 https://taotoken.net/api-keys 拿,Model ID 在模型对话页面选。三个都齐了,API 通道才能通。缺任何一个,调用会返回 401 或 model not found。
4. 验证请求与成功结果:提交次数和回滚段占用怎么测
脚本跑完不算完,得验证两件事:提交次数对不对,回滚段占用有没有被压住。先说提交次数的验证。最直接的办法是在脚本里加计数变量,每次 COMMIT 累加,最后打印。
DECLARE C_BATCH_SIZE CONSTANT PLS_INTEGER := 5000; V_COMMIT_CNT PLS_INTEGER := 0; V_TOTAL PLS_INTEGER := 0; -- 游标与行变量声明略 BEGIN -- 循环体略 IF MOD(NUM, C_BATCH_SIZE) = 0 THEN COMMIT; V_COMMIT_CNT := V_COMMIT_CNT + 1; END IF; -- 循环结束后 COMMIT; V_COMMIT_CNT := V_COMMIT_CNT + 1; DBMS_OUTPUT.PUT_LINE('commit count = ' || V_COMMIT_CNT || ', total = ' || V_TOTAL); END; /假设源表 50000 行,批次 5000,预期提交次数是 10 次循环内提交加 1 次兜底,共 11 次。如果打印出来是 1 次,说明MOD判断没生效,检查NUM是不是没累加。如果远大于预期,检查是不是批次常量被改小了。
回滚段占用的验证,跑脚本的同时在另一个会话查:
SELECT s.sid, s.username, t.used_ublk, t.used_urec FROM v$transaction t JOIN v$session s ON t.ses_addr = s.saddr WHERE s.username = 'SCOTT';used_ublk是当前事务占用的 undo 块数,used_urec是 undo 记录数。分批提交生效的话,这个值会在每个批次提交后归零再重新增长,峰值不会超过单批次的 undo 需求。如果它一路涨不回落,说明 COMMIT 没执行到,或者批次太大。
还可以查 undo 表空间的整体使用:
SELECT tablespace_name, SUM(bytes)/1024/1024 AS used_mb FROM dba_undo_extents GROUP BY tablespace_name;成功结果长这样:脚本输出committed at 5000 rows、committed at 10000 rows……直到总数;v$transaction里的used_ublk周期性回落;目标表行数和源表一致。三个都对上,才算验证通过。
如果你是通过 TaoToken API 通道调度的,验证动作可以再加一步:把脚本输出的提交次数和总行数回传给模型做一致性检查。调用模型对话接口,把输出贴进去问「提交次数是否符合预期」,模型会帮你核对。这一步不是必须的,但对自动化流水线有用。
5. 本篇常见错排查:401、ORA-01555、ORA-30036 逐个拆
报错一:401 Unauthorized。这是 TaoToken API 通道的报错,不是 Oracle 的。原因通常是 Key 没带、Key 写错、或者 Base URL 写成了带路径的地址。检查三件套:Base URL 必须是 https://taotoken.net/api,Key 从 https://taotoken.net/api-keys 复制完整,请求头里Authorization: Bearer 你的Key格式别漏 Bearer。如果用的是 Claude Code 或 Cline 这类工具,检查 settings 里的 base_url 有没有多写斜杠。
报错二:ORA-01555 snapshot too old。这是分批提交最经典的坑。原因:游标是基于一致性读的长查询,COMMIT 之后 undo 数据允许被覆盖,如果查询还需要读更早的 undo 版本,就报这个错。解决办法有三个:一是扩大 undo 表空间,二是设置UNDO_RETENTION足够大,三是把游标改成SELECT ... AS OF或者用临时表先落地。生产环境推荐开自动 undo 管理,让 Oracle 自己平衡。
报错三:ORA-30036 unable to extend segment in undo tablespace。undo 表空间满了。要么是批次太大,单批次 undo 需求超过表空间;要么是UNDO_RETENTION设太大,undo 数据不释放。先调小批次,再查dba_undo_extents看占用。如果表空间确实小,让 DBA 加数据文件。
报错四:ORA-01031 insufficient privileges。脚本里用了DBMS_LOCK.SLEEP或DBMS_OUTPUT但没授权。让 DBA 执行GRANT EXECUTE ON DBMS_LOCK TO 你的用户,或者把 sleep 那行注释掉。DBMS_OUTPUT需要SET SERVEROUTPUT ON才能看到输出。
报错五:local proxy failed。这是工具侧连 TaoToken 时的网络层报错,通常是本地代理配置和工具配置冲突。检查工具里的 base_url 是不是被本地代理拦截了,把 https://taotoken.net/api 加入直连白名单。这个报错和 Oracle 无关,别去查数据库。
排查顺序建议:先看报错前缀,ORA- 开头查数据库,401/local proxy 查 API 通道。两边分开定位,别混在一起查。把每个报错对应的检查项列成清单,下次遇到直接对号入座。
6. 把批量 DML 接入统一通道:从脚本到调度的收尾动作
到这里,PL/SQL 模板、批次参数、验证动作、报错排查都齐了。最后一步是把这套东西接入 TaoToken 的统一通道,让批量任务不再依赖某台机器的本地配置。
接入动作分两个方向。方向一,脚本生成与审查:在编码工具里配置 Base URL 和 Key,让模型帮你生成 PL/SQL 模板、检查有没有漏掉兜底 COMMIT、审查批次大小是否合理。配置入口在 https://taotoken.net/doc,里面有各工具的 settings 写法。方向二,任务调度:把批量脚本包成可调用单元,通过 API 通道触发,执行结果回传做分析。模型对话入口在 https://taotoken.net/model,可以先用它验证脚本逻辑再上生产。
长期跑批量任务的团队,建议把脚本生成、审查、调度、结果分析串成流水线,用 Coding Plan 统一管理。入口在 https://taotoken.net/coding-plan。这样批次大小调整、报错排查、提交次数核对都能在一个通道里完成,不用在多个工具间切换。
收尾提醒三个实用技巧。第一,批次大小先小后大:新脚本先用 1000 跑一遍,确认提交次数和 undo 占用正常,再逐步放大到 5000 或 10000。第二,兜底 COMMIT 不能省:循环结束后必须再 COMMIT 一次,否则最后不足一批的数据不会落库。第三,验证要成对:提交次数和回滚段占用一起看,只看一个容易漏判。
脚本跑通后,把批次常量、Base URL、Key、Model ID 记到团队的配置文档里,下次迁移直接复用。批量 DML 的分批提交不是一次性任务,是一套可以沉淀下来的模板和参数体系。