Cotato 네트워킹 회고 2 (대량의 데이터 저장하기)

minchan·2025년 1월 8일
0

Cotato 동아리

목록 보기
2/7

20만 건의 데이터를 읽어와 데이터베이스에 저장하는 로직을 구현하면서, JPA의 saveAll() 방식과 JDBC를 활용한 Bulk Insert 방식을 비교하며 겪었던 차이를 중심으로 작성한 글이다.

대량 데이터 저장 기능은 시스템 성능에 직접적인 영향을 미치기 때문에, 효율적인 쿼리 실행 방식이 필수적이다. 성능 최적화를 목표로 두 가지 방식의 장단점을 분석하고, 그 차이를 정리했다.

JPA vs JDBC

  • 스프링 데이터 JDBC 특징
    • JDBC 위에 얇은 추상화 계층을 제공, SQL 직접 제어가 가능.

    • 설정이 간단하며 성능 최적화가 중요한 애플리케이션에 적합.

    • 개발자가 SQL을 직접 작성하여 데이터베이스 작업을 세밀하게 제어 가능.

      @Repository
      public class MyRepository {
          @Autowired
          private JdbcTemplate jdbcTemplate;
      
          public List<MyEntity> findAll() {
              return jdbcTemplate.query("SELECT * FROM my_table", new BeanPropertyRowMapper<>(MyEntity.class));
          }
      }

      💡 JDBCJava에서 데이터베이스와 상호작용하기 위한 표준 API입니다. 이를 통해 Java 애플리케이션은 다양한 관계형 데이터베이스에서 데이터를 읽고 쓰는 작업을 수행할 수 있습니다.

  • JPA 특징
    • ORM 기반 자바 표준으로, 객체와 데이터베이스 테이블 간의 자동 매핑 지원.
    • 복잡한 데이터베이스 작업과 객체 관계 매핑을 자동화하여 개발 속도와 유지보수성을 높임.
    • 객체 지향적 프로그래밍 방식에 적합.
      @Entity
      public class MyEntity {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      }
      
      public interface MyRepository extends JpaRepository<MyEntity, Long> {
      }
  • 선택 기준
    • 스프링 데이터 JDBC: 성능 최적화와 SQL 제어가 중요한 경우, 단순한 데이터베이스 구조에 적합.
    • JPA: 객체 지향적 접근이 필요하거나, 복잡한 데이터 관계를 처리해야 하는 대규모 프로젝트에 적합.

JPA saveAll() vs JDBC Batch Insert 방식 비교와 성능 최적화

  • JPA saveAll() 방식의 문제점
    • JPA의 saveAll()은 편리하게 다수의 엔터티를 저장할 수 있는 메서드
    • 그러나 내부적으로 다음과 같은 한계가 존재
      • saveAll()은 입력된 엔터티 리스트의 크기만큼 개별 Insert 쿼리를 생성
      • 예를 들어, 20만 건의 데이터를 저장하면 20만 개의 Insert 쿼리가 발생하며, 데이터베이스 접근 횟수 역시 동일
      • 이는 데이터베이스 연결 자원을 많이 소모하고 성능 병목을 유발
  • JDBC를 활용한 Batch Insert 방식
    • JDBC의 batchUpdate를 활용하면 대량의 데이터를 효율적으로 삽입할 수 있음
    • Batch 처리로 쿼리 최소화
      • 하나의 SQL 템플릿을 사용하여 다수의 데이터를 한 번에 삽입
      • 예: 20만 건의 데이터를 1,000건씩 배치로 나누면 약 200번의 Insert 쿼리로 처리 가능.
      • 이는 JPA의 saveAll() 대비 데이터베이스 접근 횟수를 획기적으로 줄여줌
    • 구현 예시
      @Repository
      @RequiredArgsConstructor
      public class PostCustomRepositoryImpl implements PostCustomRepository {
      
      	private final JdbcTemplate jdbcTemplate;
      
      	// 게시글 목록 저장, BULK INSERT 방식으로 처리
      	@Transactional
      	public void saveAllByJdbcTemplate(List<Post> posts) {
      		String sql = "INSERT INTO post (title, content, name, views) VALUES (?, ?, ?, ?)";
      		jdbcTemplate.batchUpdate(sql, posts, 1000, (ps, post) -> {
      			ps.setString(1, post.getTitle());
      			ps.setString(2, post.getContent());
      			ps.setString(3, post.getName());
      			ps.setLong(4, post.getViews());
      		});
      	}
      }

참고

JPA Batch Insert API 성능 개선기 (GenerationType.IDENTITY의 한계점)

[SpringBoot] Bulk Insert 알아보기 (Insert 쿼리 최적화)

profile
chanmin

0개의 댓글

관련 채용 정보