티스토리 뷰
N+1 상황 (뇌피셜)
- 일반적인 게시판 (제목을 클릭하는,,) 은 게시글과 댓글을 한번에 가져올 필요가 없다
- 게시글 제목을 클릭을 하면 id 를 통해 해당 id에 해당하는 게시글과 그 게시글에 달린 댓글을 가져온다. 성능상의 문제는 없음. 게시글 한개에 댓글이 달려봤자.. 얼마나 달리겠어..
- 내가 생각한 N+1 문제가 생길 수 있는 곳. .? 인스타그램처럼 무한스크롤 형식이 문제될 것 같음. 왜냐,, 한개의 인스타 글에는 사진, 글, 댓글 정보등 한번에 많은 정보를 가져와야함.. 그래서 하나의 피드에 많은 연관 엔티티가 있고 EAGER 로 한번에 가져오려고,, 할 수 있음,, 밑에처럼,,
@Test
fun OnePlusNTests() {
val response = boardRepository.findAll(대충 페이징 파라미터)
println(response)
}
Board - Comments
@Entity
data class Board(
val content: String,
@OneToMany(fetch = FetchType.EAGER)
val commentList: MutableList<Comment> = mutableListOf()
)
@Entity
data class Comment(
val comment: String,
@ManyToOne
val board: Board
)
board 2개, board id가 1인 게시글의 댓글 리스트에 2개, board id가 2인 게시글의 댓글 리스트에 1개 이렇게 데이터 값을 넣고
boardRepository에서 findAll 을 호출하니 쿼리문이 1개가 아닌 3개가 나왔다.
먼저 Board 를 조회를 하고, 게시글 수(N) 만큼 comment 테이블을 조회하는 쿼리문이 실행되는 것을 볼 수 있다.
JOIN 을 이용하면 쿼리문 하나로 가지고 올 수 있는데 N+1 만큼 실행되니까 데이터가 많다면 성능 문제가 생길 수 있다.
Hibernate:
select
board0_.id as id1_0_,
board0_.crated_date as crated_d2_0_,
board0_.last_modified_date as last_mod3_0_,
board0_.content as content4_0_
from
board board0_
Hibernate:
select
commentlis0_.board_id as board_id1_1_0_,
commentlis0_.comment_list_id as comment_2_1_0_,
comment1_.id as id1_2_1_,
comment1_.crated_date as crated_d2_2_1_,
comment1_.last_modified_date as last_mod3_2_1_,
comment1_.board_id as board_id5_2_1_,
comment1_.comment as comment4_2_1_,
board2_.id as id1_0_2_,
board2_.crated_date as crated_d2_0_2_,
board2_.last_modified_date as last_mod3_0_2_,
board2_.content as content4_0_2_
from
board_comment_list commentlis0_
inner join
comment comment1_
on commentlis0_.comment_list_id=comment1_.id
left outer join
board board2_
on comment1_.board_id=board2_.id
where
commentlis0_.board_id=?
Hibernate:
select
commentlis0_.board_id as board_id1_1_0_,
commentlis0_.comment_list_id as comment_2_1_0_,
comment1_.id as id1_2_1_,
comment1_.crated_date as crated_d2_2_1_,
comment1_.last_modified_date as last_mod3_2_1_,
comment1_.board_id as board_id5_2_1_,
comment1_.comment as comment4_2_1_,
board2_.id as id1_0_2_,
board2_.crated_date as crated_d2_0_2_,
board2_.last_modified_date as last_mod3_0_2_,
board2_.content as content4_0_2_
from
board_comment_list commentlis0_
inner join
comment comment1_
on commentlis0_.comment_list_id=comment1_.id
left outer join
board board2_
on comment1_.board_id=board2_.id
where
commentlis0_.board_id=?
[Board(content='boards'), Board(content='boards2')]
N+1 문제를 해결하는 방법은 다음과같다.
QueryDsl 을 이용한 Fetch JOIN
QueryDsl을 이용해서 Fetch JOIN 을 하는것으로 해결할 수 있다.
class BoardRepositorySupportImpl(
private val queryFactory: JPAQueryFactory
): QuerydslRepositorySupport(Board::class.java), BoardRepositorySupport {
override fun findAllBoardFetchJoin(pageable: Pageable): List<Board> {
return queryFactory.selectFrom(QBoard.board)
.leftJoin(board.commentList, comment1)
.fetchJoin()
.orderBy(OrderSpecifier(Order.DESC, QBoard.board.cratedDate))
.fetch()
}
}
fetch는 리스트로 결과를 반환하기 위해서 사용
일반 join과 Fetch Join 차이점
- fetch join : 조회 주체가 되는 엔티티 외에 연관 엔티티도 함께 SELECT 하여 영속화
- 일반 join : 연관 엔티티에 join 을 걸어도 조회 주체 엔티티만 조회해서 영속화
여튼 fetchjoin 으로 바꾼 후 해당 메서드로 게시글 전체 조회를 한 결과 다음과 같은 쿼리가 실행됨
select
board0_.id as id1_0_0_,
comment2_.id as id1_2_1_,
board0_.crated_date as crated_d2_0_0_,
board0_.last_modified_date as last_mod3_0_0_,
board0_.content as content4_0_0_,
comment2_.crated_date as crated_d2_2_1_,
comment2_.last_modified_date as last_mod3_2_1_,
comment2_.board_id as board_id5_2_1_,
comment2_.comment as comment4_2_1_,
commentlis1_.board_id as board_id1_1_0__,
commentlis1_.comment_list_id as comment_2_1_0__
from
board board0_
left outer join
board_comment_list commentlis1_
on board0_.id=commentlis1_.board_id
left outer join
comment comment2_
on commentlis1_.comment_list_id=comment2_.id
order by
board0_.crated_date desc
'1일 1삽질' 카테고리의 다른 글
[Mysql] this is incompatible with sql_mode=only_full_group_by 해결 방법 (0) | 2022.08.13 |
---|---|
Kotlin 에서 RequestDto 에 @Valid 적용하기 (0) | 2022.08.07 |
JPA OneToMany, ManyToOne 양방향 관계에서 ToString StackOverflow 해결 방법 (0) | 2022.07.23 |
[JUnit5] unsatisfied dependency mockmvc (0) | 2022.06.30 |
[Spring] CommandAcceptanceException 에러 (0) | 2022.05.31 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 파일업로드설정
- ddl-auto
- Til
- CodeDeploy
- 람다식
- spring
- Java
- TCP
- ORA-27125
- Oracle
- N+1
- AWS
- 트랜잭션격리성
- nginx
- 네이버클라우드
- OS
- graphql
- 프로그래머스
- SpringGraphQL
- JPA
- 트랜잭션
- EC2
- 기술면접
- 인덱스
- level0
- db
- Travis CI
- 운영체제
- SpringSecurity
- ci/cd
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 |
글 보관함