java - 如何超快速地插入 100,000 个父行,每个行有 200 个子行?

标签 java database jpa

我有一个名为 OrderEvent 的父实体和名为 PreCondition 的子实体。一个 OrderEvent 可以有多个前提条件(>= 200)。我需要保存 100000 OrderEvent + 100000 * 200 PreCondition。我使用 Repository.save(list Of OrderEvents) 并将每 1000 条记录保存到数据库中。插入 1000 个 OrderEvents 大约需要 30 秒。

保存所有 100000 个 OrderEvents 需要将近一个小时。

有什么办法可以将时间缩短到2分钟以下吗?

尝试了存储库的保存实体方法

    public  void parseOrder(String path, String collectionName) throws ParseException {
        BufferedReader reader;
        Connection conn = (Connection) em.unwrap(java.sql.Connection.class);
        System.out.println(conn);
        try {
            reader = new BufferedReader(new FileReader(
                    path));
            String line = reader.readLine();

            String jobNumber =  line.substring(0, 7).trim();
            String recordType =  line.substring(7, 9).trim();
            Integer len = line.length();
            preId = 0L;
            postId = 0L;
            eventId = 0L;

            OrderEvent orderEvent = this.paraseHeader(line,len,jobNumber,collectionName);
            Integer count = 1;
            Integer batch = 0;
            long startTime = System.nanoTime();

            List<OrderEvent> list = new ArrayList<OrderEvent>();
            while (line != null) {
                line = reader.readLine();
                if (line == null) {
                    continue;
                }
                jobNumber =  line.substring(0, 7).trim();
                recordType =  line.substring(7, 9).trim();
                len = line.length();

                if (recordType.equals("0H")) { 

                    count++;
                    batch++;
                    if (batch.equals(1000)) {
                        orderRepository.save(list);
                        list.clear();
                        long estimatedTime = System.nanoTime() - startTime;
                        System.out.println("Processed " +  batch + " records in " +  estimatedTime / 1_000_000_000.  +  " second(s).");

                        batch = 0;
                        startTime = System.nanoTime();
                    }


                    list.add(orderEvent);
                    //orderRepository.saveAndFlush(orderEvent);
                    orderEvent = this.paraseHeader(line,len,jobNumber,collectionName);

                } else if (recordType.equals("2F")) { 
                    this.paraseFeature(line,len,jobNumber,orderEvent);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private  OrderEvent paraseHeader (String line,Integer len,String jobNumber,String collectionName) throws ParseException {

            String model = line.substring(9, 16).trim();
            String processDate =  line.substring(len-11,len-3).trim();
            String formattedProcessDate =  processDate.substring(0,4) + "-" + 
                    processDate.substring(4,6) +"-" + processDate.substring(6,8) + " 00:00:00";

            //eventId++;

            OrderEvent orderEvent = new OrderEvent(jobNumber,UUID.randomUUID().toString(),collectionName,
                    formatter.parse(formattedProcessDate));

        //  preId++;
            //postId++;
            orderEvent.fillPrecondition("Model", "Stimulus", "OP_EQ", model);
            orderEvent.fillPostcondition("Add_Fact","Coded","Response","True");


            return orderEvent;
    }
    private  void paraseFeature (String line,Integer len, String jobNumber, OrderEvent orderEvent) {

    //  preId++;
        String feature = line.substring(len-7,len).trim();
        orderEvent.fillPrecondition("Feature", "Stimulus", "OP_EQ", feature);
    }

最佳答案

这通常取决于数据库设置,例如客户端的延迟是多少,表上的索引是什么,查询如何锁定表等等。

确保您了解网络操作花费了多少时间。这可能是限制因素,特别是如果您的数据库位于世界的另一端。

首先确定客户端和数据库服务器之间的延迟是多少。如果是 10 毫秒,那么逐行插入这一行将是:100,000 * 200 * 10ms = 200000s ~ 56h。这非常慢,因此请确保您使用 JDBC 进行批量插入。

有时,通过创建影子表可以显着加快插入过程:

  1. 创建与 OrderEventsPreCondition 表相同的新表。某些 RDBMS 允许使用 CREATE TABLE ... AS SELECT ... FROM ... 语法。
  2. 禁用影子表上的外键和索引。
  3. 批量插入所有数据。
  4. 在影子表上启用外键和索引。这有望确保导入的数据正确。
  5. 从影子表插入实际表,例如通过运行INSERT INTO ... SELECT ... FROM ...
  6. 删除影子表。

但是,最好的选择是跳过 JDBC 并切换到数据库提供的批量加载实用程序,例如Oracle DB 有 External TablesSQL*Loader 。这些工具专门设计用于高效地获取大量数据,而 JDBC 是通用接口(interface)。

关于java - 如何超快速地插入 100,000 个父行,每个行有 200 个子行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55418967/

相关文章:

java - Spring MVC @Controller 方法签名

database - 您如何处理多个开发人员和数据库更改?

java - 对于这种情况有什么正确的注释。 JPA。 hibernate

java - 如何获取 LinkedList 中给定开始和结束日期时间之间的日期时间元素范围

java - 在 UCanAccess 中通过 DDL 创建的表无法在 Access 本身中打开

database - 您是否对数据库项目使用源代码管理?

mysql - JPQL只返回第一条记录

java - 如何使用 JPA 表示 Map<EntityType, Integer> 关系?

java - 在 Java 中赋值?

ruby-on-rails - 如何在 Ruby on Rails 中将数组持久保存到数据库?