N+1, 직접 겪어보니까
N+1 문제는 JPA 공부할 때 한 번씩 다 마주친다. "조심해야지" 하고 넘어간다. 그리고 잊어버린다.
나도 그랬다.
SSAFY 때 기억
처음 N+1을 직접 부딪힌 건 SSAFY 프로젝트 때였다. 팀 프로젝트에서 게시글 목록을 조회하는데 뭔가 느리다 싶었다. 쿼리 로그를 켜봤더니 게시글 20개를 가져오는데 쿼리가 21번 나가고 있었다.
@ManyToOne으로 연결된 작성자 정보를 게시글마다 따로 조회하고 있었던 거다.
fetch = FetchType.LAZY로 바꾸고, @EntityGraph로 한 번에 join해서 가져오도록 고쳤다. 쿼리가 1번으로 줄었다. 그때 뭔가 개발자다운 문제를 해결한 것 같은 기분이 들었다. JPA의 동작 방식을 이해하고 최적화한 느낌.
근데 또 똑같이 만들었다
최근에 블로그 API 만들면서 Post 목록에 태그 정보를 같이 내려주는 기능을 짰다. Post와 Tag가 @ManyToMany로 연결되어 있었다.
혹시나 싶어서 쿼리 로그를 켜봤다.
Hibernate: select p1_0.id, ... from post p1_0
Hibernate: select t1_0.post_id, ... from post_tag pt join tag t on ... where pt.post_id=?
Hibernate: select t1_0.post_id, ... from post_tag pt join tag t on ... where pt.post_id=?
Hibernate: select t1_0.post_id, ... from post_tag pt join tag t on ... where pt.post_id=?
...
Post 10개 조회하는데 쿼리 11번. 완벽한 N+1이었다.
SSAFY 때 분명히 고쳐본 문제인데. 시간이 지나니까 잊어버렸다.
해결은 간단하다
@Query("select p from Post p left join fetch p.tags where p.status = 'PUBLISHED'")
List<Post> findAllWithTags();join fetch로 한 번에 가져오면 된다. 또는 @EntityGraph를 쓰거나.
@EntityGraph(attributePaths = {"tags"})
List<Post> findByStatus(PostStatus status);쿼리는 1번으로 줄었다.
왜 자꾸 잊어버릴까
JPA가 편하기 때문이다. SQL 직접 안 써도 되니까 실제로 어떤 쿼리가 나가는지 신경을 덜 쓰게 된다. 문제가 생기기 전까지는.
쿼리 로그는 개발 중에 항상 켜두는 게 맞다.
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace직접 보고 있어야 감이 생긴다. 안 보면 또 잊는다. 이 글도 그래서 쓴다.
$cat comments/n-plus-one0 entries
no comments yet.