[DB] Batch Update에 대해 알아보자

LIfeIsMoNgle·2025년 12월 15일
post-thumbnail

Batch Insert/Update 란

다량의 데이터를 한 번에 삽입하는 방법입니다.

INSERT INTO user (name, email) VALUES ('User A', 'a@test.com');
INSERT INTO user (name, email) VALUES ('User B', 'b@test.com');
INSERT INTO user (name, email) VALUES ('User C', 'c@test.com');
INSERT INTO user (name, email) VALUES 
('User A', 'a@test.com'), 
('User B', 'b@test.com'), 
('User C', 'c@test.com');

개별 Insert의 경우 각 쿼리 별로 DB I/O를 해야하지만, Batch Insert는 하나의 쿼리문으로 여러 데이터를 처리하기 때문에 성능이 뛰어납니다.

JPA와 Batch Insert

보통 JPA, MySQL을 사용할 때 위 코드처럼 IDENTITY 전략을 선택하여 PK 값을 자동으로 증가시키는 방식을 사용합니다.

@Entity
@NoArgsConstructor
public class Member{
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
}

JPA의 구현체인 Hibernate는 IDENTITY 전략을 사용할 때 Batch Insert가 동작하지 않습니다.
Hibernate는 엔티티를 식별자(ID) 기반으로 영속성 컨텍스트에 관리하는데, IDENTITY 전략은 INSERT를 수행해야만 ID를 알 수 있어 INSERT를 지연하거나 모아둘 수 없기 때문입니다.

		
	public class Member {
		    @Id
		    @GeneratedValue(strategy = GenerationType.IDENTITY)
		    private Long id;
		    ...
    }
    public class TestEntity {
		
		    @Id
		    @GeneratedValue(strategy = GenerationType.SEQUENCE)
		    private Long id;
		    ...
    }
    
	@Test
    @Transactional
    void 하이버네이트_테스트() throws InterruptedException {
        Member member = new Member("testUser");
        memberRepository.save(member);
        
        System.out.println("before first sleep");
        sleep(10000);
        
        TestEntity testEntity = new TestEntity("aaaa");
        testEntityRepository.save(testEntity);

        System.out.println("before second sleep");
        sleep(10000);
    }

이와 같이 ID 생성 전략을 IDENTITY로 한 경우엔 쓰기 지연이 동작하지 않고 INSERT 쿼리가 바로 나가는 것을 확인할 수 있습니다.

SEQUENCE인 경우에는 DB

select next value for test_entity_seq

위 쿼리로 ID 값만 받아와서 엔티티에 채워 넣고, 1차 캐시에 저장합니다. 그 후 트랜잭션이 끝날 때, INSERT 쿼리가 수행됩니다.

IDENTITY가 아닐 경우에는 옵션을 통해 Batch Insert를 사용할 수 있습니다.

spring.jpa.properties.hibernate.jdbc.batch_size=N

JdbcTempate을 이용한 Batch Insert 구현

MySQL에서 Batch Insert를 구현하기 전 DB URL에 'rewriteBatchedStatements=true' 파라미터를 추가해야 합니다. true로 설정하지 않으면 Insert 쿼리가 단건으로 수행됩니다.

String sql = "INSERT INTO user (name, email) VALUES (?, ?)";

jdbcTemplate.batchUpdate(sql,
    users,
    1000, // batch size
    (ps, user) -> {
        ps.setString(1, user.getName());
        ps.setString(2, user.getEmail());
    }
);
String sql = "INSERT INTO user (name, email) VALUES (?, ?)";

jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {

    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        User user = users.get(i);
        ps.setString(1, user.getName());
        ps.setString(2, user.getEmail());
    }     

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

두가지 방식으로 Batch Insert를 구현할 수 있습니다.

profile
와타나베

0개의 댓글