千万级Oracle迁移卡在JdbcCursorItemReader上,主因是默认未设fetchSize导致驱动一次性拉取全量数据引发OOM;须显式设fetchSize为500–2000、关闭自动提交、限定查询字段并排除CLOB/BLOB。

千万级Oracle迁移卡在JdbcCursorItemReader上?
直接用默认配置的 JdbcCursorItemReader 读取千万行 Oracle 表,大概率会 OOM 或连接超时。Oracle 的游标(尤其是 REF CURSOR)在 JDBC 驱动层默认启用缓存、不支持真正流式读取,加上 Spring Batch 默认未设 fetchSize,驱动会一次性把结果集全拉进内存。
必须显式控制底层 JDBC 行为:
-
fetchSize设为 500–2000(实测 Oracle 19c + ojdbc8 下 1000 较稳),过大会占内存,过小会增加网络往返 - 关闭自动提交:
setAutoCommit(false)(由 Spring Batch 事务管理接管,但驱动层需配合) - 使用
ResultSet.TYPE_FORWARD_ONLY和ResultSet.CONCUR_READ_ONLY,避免驱动偷偷缓存滚动结果集 - SQL 中避免
SELECT *,只查迁移所需字段;若含 CLOB/BLOB,务必单独处理或排除——它们会触发额外 LOB 定位器加载
写入端用JdbcBatchItemWriter还是自定义?
千万级写入,JdbcBatchItemWriter 是起点,但开箱即用容易翻车:它默认每 chunk 提交一次事务,而 Oracle 对高频小事务极其敏感(redo log 刷盘压力大、UNDO 段争用)。
关键调整点:
- chunk size 不要盲目调大——设为 5000~10000 是平衡点;再大可能触发 Oracle 单次 DML 的 bind 变量上限(如 ORA-01000: maximum open cursors exceeded)
- 必须开启
useSharedExtendedAttributes=true(ojdbc8+),否则批量绑定参数时可能报 ORA-01795 - 禁用
isUseColumnMetaData=false,跳过驱动自动查数据字典(千万级表元数据查询本身就很慢) - 如果目标表有大量索引/约束/触发器,迁移前临时 disable,结束后 rebuild ——
JdbcBatchItemWriter无法绕过这些开销
如何避免JobRepository成瓶颈?
默认基于 Oracle 的 JdbcJobRepository 在千万级任务中,每次 chunk 提交都要更新 BATCH_STEP_EXECUTION 和 BATCH_JOB_EXECUTION 表,尤其 READ_COUNT/WRITE_COUNT 字段高频 update,极易引发行锁和日志风暴。
务实解法不是“优化 SQL”,而是降频记录:
- 重写
StepExecutionListener,只在 step 开始、结束、失败时更新计数,中间 chunk 跳过updateStepExecution - 将
JobRepository切到内存实现(MapJobRepositoryFactoryBean)——仅限单机、非高可用场景,但对纯迁移类一次性任务足够安全 - 若必须持久化,把
BATCH_*表迁到独立小库(如 H2 内存库或专用轻量 PG),与业务 Oracle 物理隔离
分片(Partitioning)真能提速?什么情况下反拖后腿?
分片不是银弹。对 Oracle 单表千万行迁移,只有当满足「可均匀切分 + 切分键高基数 + 目标表无全局唯一约束冲突」时才值得上。
典型有效分片方式:
- 按主键范围分片:
WHERE id BETWEEN :min AND :max,需提前统计 min/max 并等宽划分 - 按时间分区字段分片(如
create_time >= ? AND create_time < ?),前提是表已按时间分区且统计信息准确
踩坑警告:
- 用
MOD(id, N)分片?Oracle 会全表扫描+计算,比不分片还慢 - 分片后每个 worker 连接各自查源表?没共享连接池的话,N 个分片 = N 倍数据库连接消耗,可能直接打满 Oracle 的
processes参数 - 目标表有自增主键或全局唯一索引?分片并发写入必然冲突,必须改用 UUID 或预分配 ID 段
最常被忽略的一点:分片元数据(PartitionHandler)本身依赖 JobRepository,如果 JobRepository 已是瓶颈,加了分片反而放大问题。


















