Tag: performance
이 태그가 달린 글들 "performance"
-
[JPA + Spring Mastery 09] 어디서 무엇을 쓸까 — JPA / JPQL / Native SQL 결정 트리, 측정값 4종으로 갈리는 컷
8 편을 거치며 PersistenceContext / Dirty Checking / Optimistic Lock / N+1 / IDENTITY / self-invocation / Saga·Outbox 를 측정값으로 풀었습니다. 이 마무리편은 그 모든 evidence 를 한 결정 트리 위에 올려놓습니다 — 도메인 단위인지 데이터 단위인지의 첫 갈림길로 시작해, JPA Dirty Checking 이 빛나는 곳 5 가지와 적자인 곳 5 가지, JPQL 의 중간 자리, Native / JdbcTemplate / MyBatis 의 자리를 4 측정값 (Dirty Checking 132× / saveAll IDENTITY ~1000× / OFFSET 1M 570× / bulk JPQL vs Dirty Checking 84×) 으로 컷하고, Eric Evans Aggregate (DDD 2003), Fowler PoEAA Identity Map / Unit of Work (2002), Greg Young CQRS Documents (2010), Vlad Mihalcea High-Performance Java Persistence (2016) 의 학술 anchor 와 카카오페이 readOnly QPS 58% 회고로 묶었습니다. 면접에서 'JPA 어떻게 다뤄봤어요?' 에 URL 한 줄로 답하기 위한 entry point 입니다.
-
@Transactional(readOnly = true) 한 줄이 응답시간을 132배 줄인 이야기 — JPA Dirty Checking 의 정체 해부
Updated:JPA dirty checking 비용을 1만 row UPDATE 6 시나리오로 분해. 비교의 *기준* 을 분리해서 읽어야 한다 — S1(일반) vs S2(readOnly SELECT) ≈ 132× 는 *readOnly 가 빠진 메서드가 부담하는 비용*, S4(@DynamicUpdate) vs S6(raw JDBC) ≈ 68× 는 *@DynamicUpdate 만으로는 dirty checking dominant 비용을 못 줄인다* 는 의미, S4 vs S5(bulk JPQL) ≈ 50× 는 *모델 자체를 dirty checking 에서 bulk 로 바꿨을 때* 효과, S5 vs S6 ≈ 1.32× 는 JPA 추상화 오버헤드 30% 수준. @DynamicUpdate 는 변경 컬럼 조합마다 SQL string 이 달라져 batch_update 효과를 약화시킬 수 있고 Query Plan Cache 압박을 일으킬 수 있어 *국지적 도구*. loadedState / FlushMode MANUAL / SPR-16956 / bytecode enhancement 까지 Hibernate 6 내부 구조와 함께, 단정 표현은 톤다운한 결론으로 정리한 v2 [실측 — Java/Spring Stage 2, EXP-13].
-
saveAll() 이 1만 INSERT 가 되는 이유 — IDENTITY + Hibernate batch 비활성화의 구조적 함정
hibernate.jdbc.batch_size=50 으로 설정해도 1만 row 의 saveAll() 이 1만 INSERT 로 발사되는 이유. GenerationType.IDENTITY 는 매 INSERT 마다 LAST_INSERT_ID() 가 즉시 필요해서 Hibernate 가 batch 를 *구조적으로 비활성화* — Statement.RETURN_GENERATED_KEYS 가 batch 와 호환 안 됨. TABLE 전략 시뮬레이션 (애플리케이션이 ID 미리 발급) 은 batch 묶임 → 200 SQL. raw JDBC batchUpdate + rewriteBatchedStatements=true 는 multi-value INSERT 로 약 10 SQL — 가장 빠름. DZone 의 IDENTITY → SEQUENCE 100배 글은 PostgreSQL 기준 — MySQL 은 SEQUENCE 미지원 (TABLE 로 에뮬레이션). MySQL 환경의 진짜 답은 UUID / TableGenerator pooled-lo / Snowflake / raw JDBC batch 중 선택입니다.
-
JPA N+1 + JOIN FETCH 깊이 함정 4종 — MultipleBagFetchException, Pagination OOM, OneToOne LAZY 트랩
4-depth 도메인 (owner→merchant→rule→history) 에서 findAll() + 자식 접근하면 21 SQL. JOIN FETCH 한 단계로 1 SQL. 단 *두 collection 동시 fetch* 면 MultipleBagFetchException — Hibernate 가 List(Bag) 두 개의 cartesian product 를 거부. *JOIN FETCH + setMaxResults* 면 HHH000104 경고 + in-memory pagination — 1만 row 다 읽고 20개만 남기는 OOM. *@OneToOne non-owning LAZY* 는 프록시가 null 인지 알 방법이 없어서 *항상 fetch* — LAZY 무시. @BatchSize 가 N+1 → N/K+1 으로 완화하는 표준 처방. JPA 의 fetch 함정은 기능 한 줄이 아니라 *Bag/List/Set + 프록시 메커니즘 + Hibernate 의 cartesian 처리 정책* 의 상호작용입니다.