Java 在spring批处理应用程序中执行多线程插入时,如何使用序列表确保oracle数据库中的顺序主键

Java 在spring批处理应用程序中执行多线程插入时,如何使用序列表确保oracle数据库中的顺序主键,java,oracle11g,spring-batch,Java,Oracle11g,Spring Batch,我有一个数据聚合和加载应用程序,可以将数据文件加载到oracle数据库。我们使用spring集成进行文件传输,使用spring批处理将数据加载到数据库。当处理多个文件(读取并加载到数据库)时,主键倾向于跳过一些值。 已创建Oracle序列 CREATE SEQUENCE "SCHEMA"."SEQUENCE_TABLE_NAME" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 2

我有一个数据聚合和加载应用程序,可以将数据文件加载到oracle数据库。我们使用spring集成进行文件传输,使用spring批处理将数据加载到数据库。当处理多个文件(读取并加载到数据库)时,主键倾向于跳过一些值。 已创建Oracle序列

CREATE SEQUENCE  "SCHEMA"."SEQUENCE_TABLE_NAME"  MINVALUE 1 MAXVALUE 
    9999999999999999999999999999 INCREMENT BY 1 START WITH 241488161 CACHE 
    20 NOORDER  NOCYCLE ;
入站通道适配器具有作为任务执行器的轮询器。入站通道适配器将文件发送到转换器,该转换器创建由作业启动网关启动的JobLaunchRequest对象。 该作业有一个读卡器和一个执行以下语句的jdbcwriter

<bean id="itemWriter" class="org.springframework.batch.item.database.JdbcBatchItemWriter">
    <property name="dataSource" ref="dataSource"/>
    <property name="sql">
        <value>
            <![CDATA[
            insert  into data_table (id,dataA,dataB)
            values(SEQUENCE_TABLE_NAME.nextval,:dataA,:dataB)
            ]]>
        </value>
    </property>
    <property name="itemSqlParameterSourceProvider">
        <bean
             class="org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider"/>
    </property>
</bean>

加载到数据库的关键原因是公开了一个api,它取决于主键的顺序和顺序。但一旦文件夹中出现两个或多个文件。由于多线程读取和写入数据库。序列表提供的主键之间缺少一些值


首先通过API公开主键通常是不好的做法

但这里有另一种说法:

让所有线程在原始表中创建这些行,而根本不关心主键。创建一个新表,该表在原始表上有一个外键引用,第二列用于存储顺序值。单个线程将从原始表中连续拾取行,并使用序列在新表中创建行。然后将新表的顺序值作为API中的键公开

因为只有那个线程在访问序列,所以应该没有间隙。如果您不期望可以想象的最高负载,那么单个线程应该能够处理多个其他线程的负载

将引入延迟,直到项目对API可见,因为现在涉及第二个事务

您应该创建在中运行的序列的自己的实现 交易

试着这样做:

CREATE TABLE schema.sequence_table ( seq BIGINT );

INSERT
  INTO schema.sequence_table
  VALUES (0);

CREATE OR REPLACE
  FUNCTION get_next_seq()
  RETURN BIGINT
  AS
DECLARE
  s BIGINT;
BEGIN
  SELECT COALESCE(seq) + 1
    INTO s
    FROM schema.sequence_table;
  UPDATE schema.sequence_table
    SET seq = s;
  RETURN s;
END;

IMHO,如果要求列中不存在间隙,则不应使用主键序列,因为主键必须在
INSERT
时间存在,并且如果事务稍后中止,则将获得间隙


一个简单的解决方案是有一个与主键不同的专用列,在单线程(或专用的单线程应用程序)成功插入后更新该列。此任务将获取最近插入的行,例如当前序列值减去阈值(如果表包含大量行,则加快请求速度)后的主键,并将专用列值设置为null,为它们分配连续值,提交并迭代。每天在负载最低的时候,任务应该获取所有在专用列(非索引查询)上可能有空值的行,作为一个全面捕获过程。

我认为您的问题在于
缓存20
,尝试将其减少到1…@UsagiMiyamoto我们已经尝试将提交间隔设置为与缓存大小类似的值。然后尝试“无缓存”以完全不使用缓存,但效果相同。但请注意,我们有超过30000条记录的文件每2-5分钟上传一次。禁用缓存意味着每次插入时都要进行读取disk@ScaryWombat我的代码不应该在意。但正如我所说的。向第三方公开数据的RESTAPI需要顺序主键。如果存在缺失值,则会创建审核问题。@OscarMakala感谢您的澄清。缺失值也可能由错误创建:
ROLLBACK
不会回滚序列。因此,您应该创建一个在事务中运行的序列的自己的实现…@OscarMakala您的客户机真的关心写入原始行和新表中的行之间的几毫秒吗?如果他真的这样做了,那么让原始请求等待,直到编写新表的线程获取线程条目。一些流在一天内加载了多达1亿条记录。请注意,有多个流,创建额外的表也会影响存储量。让一个线程处理这么多的数据,它可能无法在允许的窗口内完成处理。@OscarMakala但即使每天有1亿条记录,平均每秒也只有1000条记录。编写新表的单线程可以很好地使用jdbc批处理。应该确保这不会导致oracle进行太多的等待。否则,所有涉及此的事务都可能按顺序执行。由于事务跨越了条目的整个处理过程,因此这可能是一个瓶颈。