나만의 개발 로그 | 고민 로그
왜 JPA에서 @DynamicInsert를 쓰면 Statement 캐싱 효율이 떨어질까? 본문
1. 일반 INSERT vs. Dynamic Insert
| 케이스 | 실제로 DB에 보내지는 SQL |
| 기본(JPA 기본값) | sql INSERT INTO user (id, name, created_at) VALUES (?, ?, ?) → 모든 컬럼을 명시하고, null 값이면 ? 자리에 NULL이 넘어감 |
| @DynamicInsert 적용 | 상황 ① created_at 이 null sql INSERT INTO user (id, name) VALUES (?, ?) 상황 ② created_at 이 이미 세팅됨 sql INSERT INTO user (id, name, created_at) VALUES (?, ?, ?) |
- 컬럼마다 값이 null 인지 아닌지에 따라 SQL 문자열 자체가 달라진다.
2. Statement 캐싱이란?
- JDBC 드라이버(또는 커넥션 풀/Hibernate)는 SQL 문자열을 키로 PreparedStatement를 캐싱
- 같은 SQL이 다시 오면 서버 · 클라이언트 모두 “파싱 → 플랜 생성” 단계를 건너뛸 수 있어 속도와 CPU가 절약
[SQL 문자열] --------------> [PreparedStatement 캐시] 재사용 (같은 문자열일 때만!)
3. 캐싱 효율 ↓ : 이유
- @DynamicInsert 때문에
- INSERT INTO user (id, name) VALUES ...
- INSERT INTO user (id, name, created_at) VALUES ...
- (null 컬럼 조합이 더 있으면 더 많은 변형…)
- SQL 문자열이 매번 달라지므로 캐시 미스가 발생 → 매 INSERT마다
- 파싱
- 실행 계획 생성
- PreparedStatement 객체 새로 생성
⇒ 미세하지만 빈번하면 누적 비용이 커짐
특히 대량 배치 INSERT나 TPS가 높은 서비스에서는 차이가 눈에 보일 수도 있다고 한다!
4. 그럼 써도 되나, 말아야 하나?
| 판단 기준 | 권장 |
| DB 디폴트값 활용 빈도 | Audit 컬럼(now, uuid)처럼 항상 DB에서 자동 채워지는 값이면 @DynamicInsert 를 쓰지 않고 애초에 코드에서 값을 빼도 됨 -> now(), uuid() 등 DB에서 디폴트로 넣어주는건 SQL 고정 -> 캐싱 |
| INSERT 빈도 | 트래픽 낮고 디폴트 컬럼이 많으면 @DynamicInsert 사용 OK -> 성능 하락은 미미하지만 코드 수정 없이 DB 디폴트 쓰는 편안함은 크기 때문 |
| 성능 민감 | 대량 INSERT · 고TPS라면 정적 SQL 추구하기(캐싱 100%) + 코드에서 null/기본값 처리 권장 |
예시)
// 1) 트래픽 높음. createdAt 값을 코드에서 직접 넣어준다.
public User create(String name) {
User u = new User(name, LocalDateTime.now()); // createdAt 채움
return userRepo.save(u); // @DynamicInsert 없이도 OK (고정 SQL)
}
// 2) 트래픽 낮음. DB가 알아서 채우게 두고 싶다.
@Entity
@DynamicInsert
public class User {
@ColumnDefault("now()")
private LocalDateTime createdAt; // 코드에서 값 넣지 않음(null)
}
최종 정리를 하자면
- @DynamicInsert는 null 컬럼을 빼서 DB 디폴트 쓰자는 편리함을 주지만,
- SQL 문장이 경우의 수만큼 달라져 캐시 재사용률이 떨어질 수 있다.
- 빈번한 INSERT + 성능 민감 상황이면, 디폴트값을 코드에서 직접 세팅하는 것도 나쁘지 않다!
'웹 개발' 카테고리의 다른 글
| Postman 테스트? NO! 테스트 코드를 활용하자! (0) | 2023.02.17 |
|---|---|
| Lombok @NoArgsConstructor(force = true) (1) | 2023.02.16 |
| Lombok + Jackson 사용 시 boolean 필드 네이밍 주의(isIsActive...!?) (0) | 2023.02.15 |
| 배열 <깊은 복사, 얕은 복사> (0) | 2023.01.13 |
| REST API에서 Path Variable과 Query Parameter의 차이 + JWT 기반 인증 전략 (Access Token / Refresh Token) (0) | 2023.01.12 |
Comments