java - 如何优化从平面文件到 BD PostgreSQL 的数据导入?

标签 java database hibernate postgresql

大家早上好,我有一个问题,你正好需要导入1400万条记录,其中包含一家公司的客户信息。

平面文件。 txt有2.8GB,我开发了一个java程序,逐行读取平面文件,处理信息放入一个对象中,然后插入到PostgreSQL数据库中的一个表中,主题是我做了计算在 112 分钟内插入了 100000 条记录,但问题是我插入了零件。

public static void main(String[] args) {  

  // PROCESSING 100,000 records in 112 minutes 
  // PROCESSING 1,000,000 records in 770 minutes = 18.66 hours

  loadData(0L, 0L, 100000L);
}

/**
 * Load the number of records Depending on the input parameters.
 * @param counterInitial - Initial counter, type long.
 * @param loadInitial - Initial load, type long.
 * @param loadLimit - Load limit, type long.
 */
private static void loadData(long counterInitial, long loadInitial, long loadLimit){
  Session session = HibernateUtil.getSessionFactory().openSession(); 
  try{
      FileInputStream fstream = new FileInputStream("C:\\sppadron.txt");
      DataInputStream entrada = new DataInputStream(fstream);
      BufferedReader buffer = new BufferedReader(new InputStreamReader(entrada));
      String strLinea;
      while ((strLinea = buffer.readLine()) != null){
          if(counterInitial > loadInitial){
              if(counterInitial > loadLimit){
                  break;
              }
              Sppadron spadron= new Sppadron();
              spadron.setSpId(counterInitial);                
              spadron.setSpNle(strLinea.substring(0, 9).trim());
              spadron.setSpLib(strLinea.substring(9, 16).trim());
              spadron.setSpDep(strLinea.substring(16, 19).trim());
              spadron.setSpPrv(strLinea.substring(19, 22).trim());
              spadron.setSpDst(strLinea.substring(22, 25).trim());
              spadron.setSpApp(strLinea.substring(25, 66).trim());
              spadron.setSpApm(strLinea.substring(66, 107).trim());
              spadron.setSpNom(strLinea.substring(107, 143).trim());                   
              String cadenaGriSecDoc = strLinea.substring(143, strLinea.length()).trim();                    
              String[] tokensVal = cadenaGriSecDoc.split("\\s+");
              if(tokensVal.length == 5){
               spadron.setSpNac(tokensVal[0]);
               spadron.setSpSex(tokensVal[1]);
               spadron.setSpGri(tokensVal[2]);
                  spadron.setSpSec(tokensVal[3]);
                  spadron.setSpDoc(tokensVal[4]);
              }else{
               spadron.setSpNac(tokensVal[0]);
               spadron.setSpSex(tokensVal[1]);
               spadron.setSpGri(tokensVal[2]);
                  spadron.setSpSec(null);
                  spadron.setSpDoc(tokensVal[3]);
              }
              try{
                  session.getTransaction().begin();
                  session.save(spadron); // Insert
                  session.getTransaction().commit(); 
              } catch (Exception e) {
                  session.getTransaction().rollback();
                  e.printStackTrace();
              }
          }
          counterInitial++;
      }
      entrada.close();  
  } catch (Exception e) {
      e.printStackTrace();
  }finally{
      session.close();
  }
}

主要问题是如果他们在我插入前一百万条记录时检查我的代码,参数如下:loadData (0L, 0L, 1000000L);

问题是,当您插入以下记录时,在这种情况下,下一百万条记录将是:loadData (0L, 1000000L, 2000000L); 什么会导致它回滚前 1000 亿条记录,然后当计数器最近的值为 1000001 时将开始插入以下记录,有人可以给我一个更优化的插入记录的建议,知道有必要对待前面代码中显示的信息。

最佳答案

参见 How to speed up insertion performance in PostgreSQL .

您应该做的第一件事是绕过 Hibernate。 ORM 很方便,但您要为这种方便付出速度上的代价,尤其是批量操作时。

您可以使用 JDBC PreparedStatement 将插入分组到合理大小的事务中,并使用多值 insert

但就我个人而言,我会 use PgJDBC's support for the COPY protocol更直接地进行插入。打开 Hibernate Session 对象以获取底层 java.sql.Connectionget the PGconnection interface for it , getCopyAPI() 获取CopyManager,并使用copyIn将您的数据输入数据库。

由于看起来您的数据不是 CSV 格式而是固定宽度的字段格式,您需要做的是启动一个线程从文件中读取数据,将每个数据转换为适用于 PostgreSQL 的 CSV 格式输入,并将其写入缓冲区,copyIn 可以使用传递的 Reader 进行消费。这听起来比实际更复杂,并且有很多使用 java.io.Readerjava.io.Writer 接口(interface)的 Java 生产者/消费者线程实现的例子.

您可以改为为 Reader 编写一个过滤器,它包装底层文件阅读器并转换每一行。这比生产者/消费者线程要简单得多。首先将其作为首选进行研究。

关于java - 如何优化从平面文件到 BD PostgreSQL 的数据导入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23660415/

相关文章:

java - 在 Java 中调整索引图像的大小而不丢失透明度

sql - 如何在一个 MS SQL 查询中获得不同的平均值和总和值?

java - 带有 Maven 客户表单 Web 应用程序的 Spring MVC 无法正常工作

java——具有多态性和封装性的几何形状

java - 多维数组输出未在 java 中显示

java - 如何在 java 中创建具有枚举值的 map ?

database - 我是否需要对存储在数据库中的刷新 token 进行哈希处理?

android - 使用 REST 接口(interface)连接到数据库 : Cannot connect

java - Hibernate:session.createQuery 和 session 缓存

java - 在 hibernate 中的 onPostUpdateCollection 事件中获取集合中的旧值