성능 최적화(4) - 트랜잭션을 지원하는 쓰기 지연과 성능 최적화

2024. 2. 15. 20:21spring/JPA

트랜잭션을 지원하는 쓰기 지연과 JDBC 배치

SQL을 직접 다루는 경우를 생각해보자

insert(member1);

insert(member2);

insert(member3);

insert(member4);

insert(member5);

 

commit();

 

 네트워크 호출 한 번은 단순한 메소드를 수만 번 호출하는 것보다 더 큰 비용이 든다. 위 코드는 5번의 INSERT SQL과 1번의 커밋으로 총 6번 데이터베이스와 통신한다. 이것을 최적화하려면 한 번에 데이터베이스로 보내면 된다.

 

 JDBC가 제공하는 SQL 배치 기능을 사용하면 SQL을 모아서 데이터베이스에 한번에 보낼 수 있지만 코드 수정량이 많아진다. 또 비즈니스 로직이 복잡한 곳에서는 사용하기 쉽지 않다. 따라서 SQL 배치는 수천 건 이상의 데이터를 변경하는 특수한 상황에 SQL 배치 기능을 사용한다.

 

 JPA는 플러시 기능이 있으므로 SQL 배치 기능을 효과적으로 사용할 수 있다. 하이버네이트에서 SQL 배치를 적용하려면 다음과 같이 설정하면 된다. 데이터를 등록, 수정, 삭제할 때 하이버네이트는 SQL 배치 기능을 사용한다.

<property name="hibernate.default_batch_fetch_size" value="100"/>

 

속성의 값을 100으로 주면 최대 100건씩 모아서 SQL 배치를 실행한다. SQL 배치는 같은 SQL일 때만 유효하다. 중간에 다른 처리가 들어가면 SQL 배치를 다시 시작한다.

 

em.persist(new Member()); //1

em.persist(new Member()); //2

em.persist(new Member()); //3

em.persist(new Member()); //4

em.persist(new Team());      //5

em.persist(new Member()); //6

em.persist(new Member()); //7

em.persist(new Member()); //8

 

예를 들면 1234 / 5 / 678 이렇게 3번 SQL 배치를 실행한다.

 

트랜잭션을 지원하는 쓰기 지연과 애플리케이션 확장성

 

트랜잭션을 지원하는 쓰기 지연과 변경 감지 기능 덕분에 성능과 개발의 편의성이 좋아졌다. 하지만 가장 큰 장점은 데이터베이트 테이블 로우에 락이 걸리는 시간을 최소화한다는 점이다.

 이 기능은 트랜잭션을 커밋해서 영속성 컨텍스트를 플러시하기 전까지는 데이터베이스에 등록, 수정, 삭제하지 않는다. 따라서 커밋 직전까지 데이터베이스 로우에 락을 걸지 않는다.

 

update(memberA);  //update sql

비즈니스 로직1()     //update sql

비즈니스 로직2()     //insert   sql

commit();

 

JPA를 사용하지 않고 SQL을 직접 다루면 update(memberA)를 호출할 때 UPDATE SQL을 실행하면서 데이터베이스 테이블 로우에 락을 건다. 이 락은 비즈니스 로직1, 비즈니스 로직2가 모두 수행된 후 commit()을 호출할 때까지 유지된다.

=> 트랜잭션 때문

 

트랜잭션 격리 수준에 따라 다르지만 보통 많이 사용하는 격리 수준에서는 데이터베이스에 현재 수정중인 데이터를 수정하려는 다른 트랜잭션은 락이 풀릴 때까지 대기한다.

 

JPA는 커밋을 해야 플러시를 호출하고 데이터베이스에 수정 쿼리를 보낸다. 비즈니스 로직은 이미 실행되었으니 SQL을 실행하고 바로 커밋한다. 따라서 데이터베이스에 락이 걸리는 시간이 최소화 된다.

'spring > JPA' 카테고리의 다른 글

트랜잭션과 락(2) - 락 기본  (0) 2024.02.15
트랜잭션과 락(1)  (0) 2024.02.15
성능 최적화(3) - 배치 처리  (0) 2024.02.13
성능 최적화(2) - 읽기 전용  (0) 2024.02.13
성능 최적화(1) - N+1  (0) 2024.02.07