Spring Boot JPA saveAll()向数据库插入数据速度极慢

3
我正在使用Spring Boot和Hibernate创建RESTful API,用于为一个简单的Web应用程序读取.csv文件并将每一行插入到mysql数据库表中。我能够成功地完成这个任务,但是对于任何大型csv文件来说,它需要很长时间。我知道最好批量处理我的插入语句以减少交易次数,但我认为我没有在当前代码中实现这一点。这是我目前正在做的事情(虽然有效但速度非常慢):

CsvUploaderController.java

public class CsvUploaderController {

    // CsvUploader Repository definition:
    // @Repository
    // public interface CsvUploaderRepository extends JpaRepository<CityObj, Integer>
    @Autowired
    CsvUploaderRepository csvUploaderRepository;

    // gets called by front end with the .csv file data
    @PutMapping("/upload_csv")
    public List<CityObj> uploadCsv(@RequestBody Object data) {
        // unpackCSV converts an arrayList of Objects to a List of CityObj
        List<CityObj> unpackedCSV = unpackCSV((ArrayList<Object>) data);
        csvUploaderRepository.deleteAll(); // delete all rows in table currently. Not sure if this is relevent to issue

        // save all items as rows in my database
        return csvUploaderRepository.saveAll(unpackedCSV); // this is where it takes forever to complete
    }
    ...
}

application.properties:

spring.datasource.url=jdbc:mysql://url.to.my.database
spring.datasource.username=myusername
spring.datasource.password=weirdpassword
spring.datasource.hikari.maximumPoolSize = 5

spring.jpa.properties.hibernate.generate_statistics=true 
spring.jpa.properties.hibernate.jdbc.batch_size=20 // I've tried playing around with different values. Hasnt helped
#spring.jpa.properties.hibernate.order_inserts=true // I've also tried using this but not sure what it does and didnt help

我做错什么了吗?我如何提高数据插入的性能?我是否错误地理解了saveAll()方法?我的问题与这里描述的非常相似:Spring boot 2升级 - spring boot data jpa saveAll()非常缓慢


https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#batch-session-batch - JB Nizet
1
不必自己动手,Spring Batch已经提供了这个功能。 - Sagar Ahuja
1
如果你只是在单个表中执行插入操作,JPA 对你几乎没有任何帮助,反而会使过程变得更慢、更复杂。使用 JdbcTemplate 在不先将它们转换为对象的情况下执行批量插入。 - Jens Schauder
你解决了这个问题吗? - afe
2个回答

4
使用JdbcTemplate,它速度更快。
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;

public int[] batchInsert(List<Book> books) {

    return this.jdbcTemplate.batchUpdate(
        "insert into books (name, price) values(?,?)",
        new BatchPreparedStatementSetter() {

            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, books.get(i).getName());
                ps.setBigDecimal(2, books.get(i).getPrice());
            }

            public int getBatchSize() {
                return books.size();
            }

        });
}

谢谢你的建议,@Ntakadzeni Tharage。与使用JPA相比,速度快得离谱。 - Glenster

2
您可以通过调整HikariCP配置来提高MySQL性能:https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration。由于saveAll()只是在列表上循环,因此您可以手动循环,并每20个实体刷新一次以减轻持久化上下文的压力。当然,正确的批处理也会有所帮助。最快的方法将是使用JdbcTemplate和多值插入的纯SQL:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接