티스토리 뷰

반응형

IDENTITY 전략 문제점

JPA에서 아래와 같이 엔티티에 PK가 Auto Increment 되도록 IDENTITY 전략을 사용할 때가 있다.

@Entity
@Table(name = "task")
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Auto Increment 되는 PK 생성
    private Long seq;
 
 	//이하 생략
 }
}


그런데 이 전략의 최대 단점은 JPA 에서 제공하는 Bulk Insert를 사용할 수 없다는 점이다.
기본적으로는 JPA 에서 saveAll()을 통해 자동으로 Bulk Insert를 사용할 수 있지만,
IDENTITY 전략을 사용하게 되면, Insert 쿼리문이 하나씩 찍히게 된다.

그 이유는
실제로 DB에서 Insert를 수행하기 전까지는 JPA가 AutoIncrement 되는 값(PK)을 알수 없기 때문에 JPA에서 지원하지 않는다.


그래서 다 건의 데이터를 insert 할때는 JdbcTemplate을 이용하여 Insert 합치기 구문을 이용한 BulkInsert처리를 한다.


JdbcTemplate 사용하기

JdbcTemplate을 활용하면 아래와 같이 데이터를 하나의 쿼리로 한번에 삽입가능하다.

// 2건의 데이터 삽입 각각 수행
insert into task (name, status, category) value ("이름1","진행","교육");
insert into task (name, status, category) value ("이름2","완료","교육");

// 데이터 한번에 삽입
insert into task (name, status, category) value ("이름1","진행","교육"),("이름2","완료","교육");


jdbcTemplate을 사용하는 방법은 크게 어렵지 않다.
기존에 사용하는 구조에서 JdbcTemplate을 Autowired 하여 Respository를 구현하면 된다.

JdbcTemplate에서 batchUpdate를 제공하는 메소드는 여러개인데, 처음에는 BatchPreparedStatementSetter를 이용한 메소드를 사용했다가, 이상하게 성능이 나오질 안항서 argTypes까지 받는 방법으로 수정했다.

https://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/spring-jdbc-batch-update.html

Spring - JDBC Batch Update Examples

A JDBC batch update is multiple updates using the same database session. That is, we don't have to open connections multiple times. In our previous example, let's say we want to insert multiple Person objects in the database. Followings are the various way

www.logicbig.com

이 링크에 각 메소드 별로 예제가 잘나와있어서 참고하면서 구현해보았다.
아래는 그 구현 예제이다.

@Repository
@RequiredArgsConstructor
public class JdbcTaskRepository { 

    @Autowired
    private final JdbcTemplate jdbcTemplate; 

    public int[] batchInsert(List<Task> tasks){
        String sql = "insert into task (name, status, category) value (?,?,?)";  // Insert문 정의
        int[] argTypes = {Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};  // Insert문 타입 정의
        
        List<Object[]> batchArgs = new ArrayList<>();  // Insert문 Value 값 정의
        
        for(Task task : tasks){ 
        	batchArgs.add(new Object[]{
            	task.getName(),
                task.getStatus(),
                task.getCategory()
            });
        }
        
        return jdbcTemplate.batchUpdate(sql, batchArgs, argTypes); //BatchUpdate 실행
        
	}
}

JPA + JdbcTemplate 성능 올리기 - 삽입(Insert)

다건의 데이터에 대해 성능이 너무 느려서 BatchUpdate까지 쓰게 되었는데, 정작 생각보다 속도가 빠르진 않았다.
그래서 열심히 StackOverflow 헝님들에게 물어봤다.

why so slow.....???????????

https://stackoverflow.com/questions/20360574/why-springs-jdbctemplate-batchupdate-so-slow

Why Spring's jdbcTemplate.batchUpdate() so slow?

I'm trying to find the faster way to do batch insert. I tried to insert several batches with jdbcTemplate.update(String sql), where sql was builded by StringBuilder and looks like: INSERT INTO T...

stackoverflow.com



방법을 정리해보자면 아래와 같이 3가지로 나뉘었다.

1) ?useServerPrepStmts=false&rewriteBatchedStatements=true 옵션 사용
2) jdbcTemplate.batchUpdate(insert, parameters, argTypes); 메소드 사용
3) spring.jdbc.getParameterType.ignore = true 설정 사용



하나씩 해보았다.

1) useServerPrepStmts=false&rewriteBatchedStatements=true 옵션

흠 이거는 그닥 먹히지 않았다. 이 옵션이 무엇인지는 구글링했을 때 많이 나와서 설명은 생략하겠다.

2) jdbcTemplate.batchUpdate(insert, parameters, argTypes); 메소드

이 메소드는 조금은 효과가 있었다. BatchPreparedStatementSetter 파라미터를 사용한 batchUpdate 보다는 몇초 더 빨라졌다.

쉽게 설명하자면,
batchUpdate(String sql, BatchPreparedStatementSetter pss) 메소드에서는 쿼리문의 Type을 명시하지 않아서 Spring 에서 자동으로 쿼리파라미터의 타입을 찾고 Mysql을 실행해야하는데 , batchUpdate(insert, parameters, argTypes) 메소드에서는 argTypes을 개발자가 직접 명시하니 속도가 좀더 빨라진것같다.


하지만, 이 메소드를 사용했을때 이상하게 Front단에서 API를 첫번째 호출할때는 속도가 안나고 두번째, 세번째.....그 이상의 호출에서만 속도가 빨랐다. 아마 처음호출할때는 파라미터타입을 인식해야해서 첫번째호출의 속도가 안나온것같다.

3) spring.jdbc.getParameterType.ignore = true 설정

2번의 첫번째 호출 속도 저하 문제가 해당 옵션을 application.properties 파일에 추가하니깐 개선되었다. 첫번째 호출에서 파라미터를 인식하는거를 무시하겠다는 옵션이다

(자세하게 설명한 글이 있어서 퍼왔음)



결론적으로 batchUpdate를 통해 Insert를 수행할때의 속도문제는 개선되었다.




깨달음과 함께
동일하게 다른 API에서 BatchUpdate 를 사용했는데, 이번에는 속도가 느렸다...
왜냐면 이번에는 Update문이였기 때문이다.......
뜨아아ㅏㅏㅏㅏㅏㅏ

JPA + JdbcTemplate 성능 올리기 - 수정(Update)

jpa에서 JdbcTemplate을 활용해서 update를 수행한다면 쿼리문을 잘 봐야한다.
왜냐하면 JPA가 더티체킹을 해서
혼자 계속 update를 하게된다.


jdbcTemplate에서 Update쿼리문을 날리게 되면 영속성환경에서 벗어나 JPA가 알수 없는 사이에 데이터가 변경된다.
그러면 JPA는 변경감지를 해서 영속성컨텍스트를 update한다..
이로인해 성능은 다시 저하된다.


그래서!!!!
정말 무한 디버깅을 해봤다.
이리 해보고 저리해보면서 더티체킹이 안될때까지!!!!!!


그리고 드디어 해냈는데,, 역시나 해답은 단순하였다..

private final JdbcTaskRepository jdbcTaskRepository;
private final EntityManager entityManager;

@Transactional
public void updateTask(){
	....
	entityManager.clear(); // 영속성 컨텍스트에 있는 데이터를 제거
	jdbcTaskRepository.batchUpdateTasks(tasks); // update쿼리문 수행
	...
}

그냥 영속성 컨텍스트를 clear하면 되었다.ㅎㅎㅎ

clear 후에 jdbcTemplate을 이용하여 update를 수행하면 더티체킹할 일도 없었다.
이후에 해당 객체들을 쓰거나 find 해야할 필요가 없기에 깔끔하게 clear했더니, 더티체킹이 안되어다..
(flush, clear 사용은 해당 비즈니스 로직을 충분히 숙지+고민한 후에 쓰는게 좋음)

-깔끔-




jpa환경에서 영속성을 벗어나는 jdbcTemplate을 사용하는 거는 꽤나 두뇌가동이 필요한 일같다.
JOOQ를 사용하면은 JPA와는 달리 AutoIncrement 환경에서 bulk insert를 지원한다는데,, 한번 공부해봐야겠다...


그럼 20000


참고자료
https://stackoverflow.com/questions/20360574/why-springs-jdbctemplate-batchupdate-so-slow
https://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/spring-jdbc-batch-update.html
https://javabydeveloper.com/spring-jdbctemplate-batch-update-with-maxperformance/
https://velog.io/@backtony/JPA-Batch-Insert%EC%99%80-JDBC-Batch-Insert#5-jdbc-batch-insert
https://jojoldu.tistory.com/558
https://godekdls.github.io/Spring%20Data%20Access/dataaccesswithjdbc/

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함