【问题标题】:How to insert data from List into DB table using multi threading to improve performance?如何使用多线程将数据从 List 插入 DB 表以提高性能?
【发布时间】:2016-04-13 18:52:27
【问题描述】:

我必须将员工数据从文本文件(每条记录由制表符分隔)读取到 ArrayList 中。然后我必须将这个员工对象从列表中插入到数据库中的员工表中。为此,我将逐个迭代列表元素,并将员工详细信息一次插入数据库。在性能方面不推荐这种方法,因为我们可以有超过 10 万条记录,并且插入整个数据需要很长时间。

我们如何在将数据从列表插入到数据库时使用多线程来提高性能。另外我们如何使用 CountDownLatch 和 ExecutorService 类来优化这个场景。

读写测试

public class ReadWriteTest {

public static void main(String... args) {
    BufferedReader br = null;
    String filePath = "C:\\Documents\\EmployeeData.txt";
    try {
        String sCurrentLine;
        br = new BufferedReader(new FileReader(filePath));
        List<Employee> empList = new ArrayList<Employee>();

        while ((sCurrentLine = br.readLine()) != null) {
            String[] record = sCurrentLine.split("\t");
            Employee emp = new Employee();
            emp.setId(record[0].trim());
            emp.setName(record[1].trim());
            emp.setAge(record[2].trim());
            empList.add(emp);
        }
        System.out.println(empList);

        writeData(empList);

    } catch (IOException | SQLException e) {
        e.printStackTrace();
    }
}

public static void writeData(List<Employee> empList) throws SQLException {
    Connection con =null;
    try{  
        Class.forName("oracle.jdbc.driver.OracleDriver");  

        con=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","system","oracle");  
        for(Employee emp : empList)  
        {
        PreparedStatement stmt=con.prepareStatement("insert into Employee values(?,?,?)");  
        stmt.setString(1,emp.getId()); 
        stmt.setString(2,emp.getName());
        stmt.setString(3,emp.getAge());
        stmt.executeUpdate();   
        }         
        }catch(Exception e){ 
            System.out.println(e);
        }
        finally{
            con.close();
        }   
        }  
}

员工类

public class Employee {

String id;
String name;
String age;

public String getId() {
    return id;
}
public void setId(String id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public String getAge() {
    return age;
}
public void setAge(String age) {
    this.age = age;
}
@Override
public String toString() {
    return "Employee [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}

EmployeeData.txt

1   Sachin  20
2   Sunil   30
3   Saurav  25

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    直接导入

    Java 应用程序方法的替代方法是数据库方法。所有主要数据库都有可以将数据直接从文本文件导入表的工具。

    Postgres 具有COPY 命令。这可以是 run from the command line 或来自 SQL 内部。请参阅the wiki page 进行讨论。

    查看您的数据库工具集。

    【讨论】:

    • 是的,像 Postgres COPY 这样的导入工具可以非常快速执行。数据更直接地馈送到表中,而无需处理您的 SQL 语句。限制是您要导入的数据必须与目标表的结构紧密匹配,因为这些工具通常提供的处理或更改功能很少,这是它们速度快的另一个原因。作为一名程序员,我不断学习超越我的编程语言(Java 等)并利用 Postgres 等有价值的数据库平台中提供的强大功能。
    【解决方案2】:

    我同意@kuporific。从性能的角度来看,批量更新将被证明更好。

    尝试对您的代码进行以下编辑:

        public static void writeData(List<Employee> empList) throws SQLException {
        Connection con =null;
        final int BATCH_SIZE = 1000; // just an indicative number
        try{  
            Class.forName("oracle.jdbc.driver.OracleDriver");  
            con=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","system","oracle");  
            Statement statement = con.createStatement();
            int counter = 0;
            for(Employee emp : empList)  
            {
                String query = "insert into Employee (id, name, city) values('"
                        emp.getId() + "','" + emp.getName() + "','" + emp.getAge() + "')";
                statement.addBatch(query);
                if (counter % BATCH_SIZE == 0){
                    statement.executeBatch();
                }
                counter++;  
            }
    
            statement.close();
    
            }catch(Exception e){ 
                System.out.println(e);
            }
            finally{
                con.close();
            }   
    }  
    

    【讨论】:

      【解决方案3】:

      根据您的应用程序,将数据库更新代码放在主应用程序线程之外的线程上可能是有意义的。例如,您可以使用 Executors 来执行此操作。

      您也可以考虑改用batch updates

      我怀疑尝试在多个线程上更新数据库不会加快速度,因为数据库必须保持原子性,因此任何表一次只能由一个线程更新。

      你可以非常疯狂地使用 Java 8 的 CompletableFuture 在主线程之外执行这两个操作:

      CompletableFuture.supplyAsync(new Supplier<List<Employee>>()
      {
          @Override
          public List<Employee> get()
          {
              List<Employee> employees = new ArrayList<>();
              // get employee list
              return employees;
          }
      }).thenAcceptAsync(new Consumer<List<Employee>>()
      {
          @Override
          public void accept(List<Employee> employees)
          {
              // put into DB using batching
          }
      });
      

      第一个supplyAsyc 将在另一个线程上调用给定的代码。完成后,将返回值传递给thenAcceptAsync中的Consumer,并且该函数也在另一个线程上运行。

      这可以更简洁地写成:

      CompletableFuture.supplyAsync(() -> {
          List<Employee> employees = new ArrayList<>();
          // get employee list
          return employees;
      }).thenAcceptAsync(employees -> {
          // put into DB using batching
      });
      

      【讨论】:

      • 我有一个类似的用例,但我正在尝试从数据库中获取数据并将其传递到 UI。我是 CompletableFuture 的新手,因为我试图将 AsyncTask [也 ExecutorService.submit() 替换为 .get()] 以将 CardViews 列表从 Room 数据库加载到 UI。没有任何运气,如果有任何想法或想法,将不胜感激:stackoverflow.com/questions/67294345/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多