单线程Spring Batch作业读写同Oracle表是否存在隐患?
我编写了一个单线程Spring Batch Job用于修复表列数据,该作业无并行或多线程步骤,仅使用JdbcCursorItemReader流式读取数据,通过Writer修正并更新行数据,核心步骤代码如下:
@Bean("orderidFixerStep") @JobScope public Step orderidFixerStep(final JdbcCursorItemReader<Order> orderIdReader, final OrderIdItemWriter orderIdItemWriter, final JobRepository jobRepository, final PlatformTransactionManager transactionManager, final AppProperties appProperties) { return new StepBuilder("orderidFixerStep", jobRepository) .<Order, Order>chunk(appProperties.getBatchSizeToValidate(), transactionManager) .reader(orderIdReader) .writer(orderIdItemWriter) .chunk(appProperties.getBatchSizeToValidate()) .build(); }
我担忧同时对同一张表进行读和写操作是否会导致数据不一致?
我认为不会,理由是:JdbcCursorItemReader默认返回TYPE_FORWARD_ONLY类型的ResultSet,通过游标获取的数据在ResultSet中不会因行更新而改变;且我仅更新已读取的批次行,新增行不在ResultSet中也无需处理。
我的理解是否正确?是否存在遗漏的边缘场景?目前测试正常,但仍想确认是否有潜在问题。
补充数据详情:
我在某列存储每个订单ID的哈希值(一对一),订单ID列在另一张表中。部分行的订单ID已损坏,需遍历所有订单ID计算哈希并与另一表的对应哈希比对;若不匹配,则解析第三列的JSON获取原始订单ID。哈希、JSON及订单ID均为只读数据,所有操作在同一数据库中。
你的理解是正确的,单线程+TYPE_FORWARD_ONLY游标这种模式下,不会出现你担心的读-写数据不一致问题,但可以留意几个边缘场景:
数据库隔离级别影响:如果数据库采用
READ_UNCOMMITTED隔离级别,理论上可能读到未提交的更新,但你的Job是读写同一批数据,且更新逻辑基于已读取的快照,只要事务配置为默认的READ_COMMITTED及以上,这个问题就不存在。另外,游标是单向向前遍历,不会回头读取已处理过的行,所以即使行被修改,也不会影响后续读取结果。游标遍历顺序稳定性:如果JdbcCursorItemReader没有指定明确的排序条件,数据库的游标遍历顺序可能不稳定。虽然单线程下遗漏或重复处理行的概率极低,但建议加上固定排序(比如按主键ID排序),彻底消除这个风险,确保全量行都被处理一次。
“只读数据”的并发修改:你提到哈希、JSON及订单ID是只读数据,但如果有其他系统或Job在同时修改这些数据(比如运维操作、其他批处理),可能导致你的修复逻辑拿到脏数据。这种情况属于跨系统并发问题,和你的单线程Batch Job本身无关,需要通过数据库锁或业务层面的排他机制解决。
事务提交的原子性:你的Chunk配置按批次提交事务,每个批次的读取和更新在一个事务内。只要Writer是基于当前批次读取的Order对象进行更新,就不会出现批次内的数据不一致——某行更新失败会导致整个批次回滚,不会出现部分行修改成功的情况。
总结来说,你的当前实现是安全的,测试正常的前提下,只要注意以上几个边缘场景,就不会有潜在的数据一致性问题。
内容的提问来源于stack exchange,提问作者humbleCoder

