网络知识 娱乐 Spring Data JPA批量处理性能优化

Spring Data JPA批量处理性能优化

 问题:

       我们使用JPA的saveAll的时候,会发现这个方法特别慢,我在本地做的测试是插入10万条数据的时候,耗时51503ms,和JDBC方式的批量处理耗时对比后就会发现,性能差了百倍,所以我们要优化下我们的代码。

一:修改setId的策略

       我们观察就会发现,在saveAll传入的对象里面,设置了id的情况下,JPA会根据id去执行一条select语句,数据量越多,和数据库交互次数越多,这个操作会极大的拖慢性能,那我们首要解决的,就是不让JPA去执行select。解决办法有两个,麻烦一点的是这样的,实现Persistable接口,重写isNew方法,返回true时,就不会去执行select。如下代码所示,我们在给对象setId之前,调用下preInsert方法,这样就将isNew设置为true了。

@Entity
@Table(name = "user")
@Data
public class User implements Persistable {
    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "name")
    private String name;

    @Column(name = "phone_number")
    private String phoneNumber;

    @Transient
    private boolean isNewFlag;

    public void preInsert(){
        this.isNewFlag = true;
    }
    @Override
    public boolean isNew() {
        return isNewFlag;
    }
}

        还有一种实现方式是我们将手动setId改到在实体里通过注解来设置,如下所示:

    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(name = "id")
    private String id;

      这样优化后,时间缩短到了24260ms。性能还是很差,这是因为JPA的配置里没有开启批量操作。

二:开启JPA批量操作

       mysql的话,开启批量操作需要在jdbc的url后面加上参数rewriteBatchedStatements=true,oracle无需此操作。开启JPA的批量操作需要在yml里加上如下配置

  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 500
        order_inserts: true
        order_updates: true

tips:batch_size不宜配置的太大,并不是越大,性能越好。       

      这样设置后,时间缩短到了2915ms,性能提升了大约20倍。但这样和JDBC方式还是有很大的差距,如果对性能还不满意的,可以改用JDBC的批处理方式。

三:终极优化JdbcTemplate

       在Spring里,我们可以使用JdbcTemplate来简化JDBC的操作,然后通过执行jdbcTemplate里的batchUpdate方法,我们只需要传入sql语句,然后再设置下参数,就可以轻松实现批量操作。

    @Autowired
    private UserRepository userRepository;

    public void batchInsert(List list){
        String sql = "insert into user(id, name , phone_number) values(?, ?, ?)";
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                preparedStatement.setString(1, list.get(i).getId());
                preparedStatement.setString(2, list.get(i).getName());
                preparedStatement.setString(3, list.get(i).getPhoneNumber());
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        });
    }

        这种方式改造后,批量插入的耗时是185ms,性能提升200倍。。