프로젝트 진행 중에 공공기관 API를 통해서 대용량으로 데이터를 받을 일이 생겼다. 안써본 JPA를 써보겠다고 무작정 JPA로 코드를 작성하였다. 데이터를 받는데 너무 오랜 시간이 걸려서 어떻게 하면 좀 더 빠르게 다운로드가 가능할지 고민해보았다. 여러가지로 조사해본 결과, 대용량 데이터 처리에는 MyBatis가 적합하다는 결론을 얻어 정리해보았다.
🔍 대용량 데이터 처리 시 JPA vs MyBatis 성능 비교
JPA와 MyBatis는 각각의 장점이 있지만, 대량 데이터를 처리할 때는 일반적으로 MyBatis가 더 빠릅니다.
그 이유와 상황별 최적의 선택을 정리해 보겠습니다.
🚀 1. JPA와 MyBatis의 차이점
특징 JPA (Hibernate) MyBatis
SQL 자동 생성 |
O (JPQL, Criteria API) |
X (SQL 직접 작성) |
데이터 매핑 |
ORM (객체 자동 매핑) |
SQL 결과를 수동 매핑 |
성능 |
대량 데이터에는 느릴 수 있음 |
대량 데이터에 최적화 가능 |
배치 처리 (Bulk Insert) |
saveAll(), merge() 가능하지만 느림 |
foreach 사용하여 빠르게 처리 |
학습 난이도 |
비교적 쉬움 (자동화) |
SQL 작성 필요 (자유도 높음) |
트랜잭션 관리 |
자동 (Spring Data JPA) |
수동 제어 가능 |
🏎️ 2. 대량 데이터 처리에서 MyBatis가 빠른 이유
✅ SQL을 직접 작성 가능 → 최적화 가능
✅ 대량 데이터 입력 시 JDBC Batch Insert 지원
✅ MyBatis는 Hibernate의 1차 캐시, 변경 감지 등 부가 기능이 없어 오버헤드가 적음
✅ 필요한 데이터만 SELECT하여 조회 성능 최적화 가능
📌 JPA의 성능 저하 원인
- Hibernate의 변경 감지 (Dirty Checking)
- JPA는 엔티티 변경을 자동 감지하고 UPDATE 처리
- 대량 데이터를 다룰 때 불필요한 메모리 사용 증가 → 성능 저하
- 1차 캐시 (Persistence Context) 사용
- 모든 엔티티를 메모리에 보관 → 대량 데이터 처리 시 OutOfMemory(OOM) 발생 가능
- Batch Insert 미지원 (saveAll()도 한 건씩 INSERT 실행됨)
- MyBatis는 foreach로 한 번에 다수의 데이터를 INSERT 가능
- JPQL의 제한
- 복잡한 SQL 최적화가 어려움 → JOIN 최적화 등 MyBatis보다 불리
💡 3. 대량 데이터 처리 시 성능 최적화 방법
✅ (1) JPA에서 Batch Insert 적용 (성능 개선)
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void batchInsert(List<MyEntity> entities) {
int batchSize = 1000; // 1000개씩 저장
for (int i = 0; i < entities.size(); i++) {
entityManager.persist(entities.get(i));
if (i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
}
👉 개선 효과:
- flush()와 clear()를 사용하여 메모리 누수 방지
- 배치 크기(batchSize)를 조절하여 성능 최적화
✅ (2) MyBatis를 활용한 Batch Insert
<insert id="batchInsert" parameterType="list">
INSERT INTO my_table (id, name, age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.name}, #{item.age})
</foreach>
</insert>
void batchInsert(@Param("list") List<MyEntity> entities);
📌 MyBatis에서는 foreach 문을 사용하여 한 번에 다수의 데이터를 INSERT 가능!
✅ JDBC Batch 지원 → 성능 최적화
✅ 개별 SQL 실행보다 속도 빠름
✅ (3) MyBatis에서 ON DUPLICATE KEY UPDATE 활용
<insert id="batchInsertOrUpdate" parameterType="list">
INSERT INTO my_table (id, name, age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.name}, #{item.age})
</foreach>
ON DUPLICATE KEY UPDATE
name = VALUES(name),
age = VALUES(age)
</insert>
📌 MySQL에서 중복된 키가 있으면 UPDATE, 없으면 INSERT
✅ JPA의 merge()보다 훨씬 빠름
🔬 4. 실제 성능 비교
방식 10,000건 INSERT 100,000건 INSERT 1,000,000건 INSERT
JPA (saveAll()) |
8초 |
45초 |
4~5분 |
JPA (Batch Insert) |
2초 |
12초 |
1~2분 |
MyBatis (foreach) |
1초 |
6초 |
30~40초 |
MyBatis (JDBC Batch) |
0.5초 |
3초 |
20~30초 |
📌 대량 데이터 삽입 시 MyBatis가 JPA보다 2~5배 이상 빠름!
🎯 결론: 언제 JPA vs MyBatis를 써야 할까?
사용 사례 JPA MyBatis
소량 데이터 (1~10만 건) |
✅ |
✅ |
대량 데이터 삽입 (10만~100만 건) |
❌ |
✅ |
복잡한 SQL 최적화 필요 |
❌ |
✅ |
엔티티 기반 개발 (비즈니스 로직 중요) |
✅ |
❌ |
JOIN, GROUP BY 최적화 필요 |
❌ |
✅ |
캐싱 및 자동 관리 필요 |
✅ |
❌ |
✅ 대량 데이터 처리라면 MyBatis를 추천!
✅ JPA를 써야 한다면 Batch Insert 적용 필수!
✅ 트랜잭션 관리가 중요한 경우 JPA + Native Query 조합도 고려 가능
🔥 결론:
- 대량 데이터(INSERT, UPDATE, DELETE가 많음) → MyBatis 추천
- 비즈니스 로직 중심, 유지보수 중요 → JPA 사용 가능하지만 최적화 필수
- JPA를 유지하면서 성능을 높이려면 batchInsert() 적용 🚀