[JPA] Batch Insert

신명철·2022년 5월 25일
0

JPA

목록 보기
9/14

들어가며

성능 향상을 위해서 Batch Insert를 도입했다. 그 과정을 정리하기 위해서 작성한다.

Batch Insert

// 단건 insert
insert into disabled (created_time, device_id, id) values (?, ?, ?)

// 멀티 insert
insert into disabled (created_time, device_id, id) 
values 
	('2022-05-25 17:41:33.186228', 8, 25),
    ('2022-05-25 17:41:33.19223', 13, 26),
    ('2022-05-25 17:41:33.195231', 18, 27),
    ('2022-05-25 17:41:33.199232', 23, 28)

Batch Insert는 여러 개의 Insert를 묶어서 하나의 트랜잭션으로 묶는 것을 말한다.

JPA 에서는 내부적으로 쓰기 지연 SQL 저장소를 이용한다. 커밋을 하기 전까지 DB에 엔티티를 저장하지 않고 쌓아 뒀다가 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 날리게 된다.

영속 상태가 되어 관리되는 영속성 컨텍스트의 엔티티들은 트랜잭션을 커밋하면 변경 내용을 DB 와 동기화하게 된다. 이 때 수정, 삭제, 삽입과 같은 내용을 DB에 반영하게 되는 것이다.

application.yml

spring:
    datasource:
        url: jdbc:mysql://localhost:3306/capstone?serverTimezone=Asia/Seoul&rewriteBatchedStatements=true
.
.
.
jpa:
        database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
        hibernate:
            ddl-auto: create
        properties:
            hibernate:
                format-sql: true
                default_batch_fetch_size: 1000
                order_inserts: true
                jdbc:
                    batch_size: 100

rewriteBatchedStatements=true 속성을 꼭 지정해줘야 사용할 수 있다. default 값은 false이고, 해당 설정이 없다면 Batch insert는 동작하지 않는다.

MySQL Connector/J 8.0 Developer Guide : 6.3.13 Performance Extensions
Stops checking if every INSERT statement contains the “ON DUPLICATE KEY UPDATE” clause. As a side effect, obtaining the statement’s generated keys information will return a list where normally it wouldn’t. Also be aware that, in this case, the list of generated keys returned may not be accurate. The effect of this property is canceled if set simultaneously with ‘rewriteBatchedStatements=true’.

ID 자동 증가

Hibernate User Guide: 12.2. Session batching
Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.

위의 공식문서에서 알 수 있듯@GeneratedValue(strategy = GenerationType.IDENTITY)Batch Insert를 지원하지 않는다.

Whenever an entity is persisted, Hibernate must attach it to the currently running Persistence Context which acts as a Map of entities. The Map key is formed of the entity type (its Java Class) and the entity identifier.

For IDENTITY columns, the only way to know the identifier value is to execute the SQL INSERT. Hence, the INSERT is executed when the persist method is called and cannot be disabled until flush time.

For this reason, Hibernate disables JDBC batch inserts for entities using the IDENTITY generator strategy.

@GeneratedValue(strategy = GenerationType.IDENTITY)를 사용할 경우, hibernateBatch insert를 비활성화한다고 한다.

영속성 컨텍스트의 내부에서는 엔티티를 식별할 때 엔티티의 타입과 엔티티의 ID를 통해서 엔티티를 식별하는데, IDENTITY의 경우 DB에 INSERT 문을 실행해야지만 ID 값을 확인할 수 있기 때문이라고 한다.

쿼리문 확인

hibernate.show_sql:true으로 로그 결과를 확인했을 때 위와 같이 단건으로 insert 되며 Batch Insert가 진행되지 않은 것으로 보인다.

show variables like 'general_log%'; # general_log 획인
set global general_log = 'ON'; # `OFF` 경우 `ON` 으로 변경

하지만, mysql 에서 로그를 확인해 보면 정상적으로 진행되었음을 알 수 있다. 하지만 이 방법은 성능에 지장을 줄 수 있으므로, 개발 환경에서만 지정해야 한다!

insert into 
disabled (created_time, device_id, id) 
values 
	('2022-05-25 17:41:33.186228', 8, 25),
    ('2022-05-25 17:41:33.19223', 13, 26),
    ('2022-05-25 17:41:33.195231', 18, 27),
    ('2022-05-25 17:41:33.199232', 23, 28)

profile
내 머릿속 지우개

0개의 댓글