How to Avoid N+1 Queries

JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž์ฃผ ๋งŒ๋‚˜๊ฒŒ ๋˜๋Š” N+1 Query์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š”

References: jojoldu.tistory.com

What are N+1 queries?

ํ•˜์œ„ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ์ฒซ ์ฟผ๋ฆฌ ์‹คํ–‰์‹œ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ , Lazy Loading์œผ๋กœ ํ•„์š”ํ•œ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜์–ด ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ

How to Avoid N+1 Queries?

1. Join Fetch

์กฐํšŒ ์‹œ ๋ฐ”๋กœ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์€ Entity Field๋ฅผ ์ง€์ • ํ•˜๋Š” ๊ฒƒ

ex)

SELECT a FROM School a JOIN FETCH a.subjects

ํ•˜์œ„ Entity๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์™€์•ผ ํ•  ๋•Œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ex)

SELECT a from School a JOIN FETCH a.subjets s JOIN FETCH s.teacher

but, ์ด ๋ฐฉ๋ฒ•์€ ๋ถˆํ•„์š”ํ•œ ์ฟผ๋ฆฌ๋ฌธ์ด ์ถ”๊ฐ€๋˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค

์ด field๋Š” Eager ์กฐํšŒ, ์ € field๋Š” Lazy ์กฐํšŒ ๋ฅผ ํ•ด์•ผํ•œ๋‹ค ๊นŒ์ง€ query์—์„œ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ๋Š๋‚„ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿด ๋•Œ, ์•„๋ž˜์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

2. @EntityGraph

@EntityGraph์˜ attributePath์— query ์ˆ˜ํ–‰ ์‹œ ๋ฐ”๋กœ ๊ฐ€์ ธ์˜ฌ field๋ช…์„ ์ง€์ •ํ•˜๋ฉด, Lazy๊ฐ€ ์•„๋‹Œ Eager ์กฐํšŒ๋กœ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค

ex)

@EntityGraph(attributePaths = "subjects")
@Query("SELECT a FROM School a")

์œ„์™€ ๊ฐ™์ด attributePath ๋ฅผ ์ง€์ •ํ•˜๋ฉด, ์›๋ณธ ์ฟผ๋ฆฌ (SELECT a FROM School a)์˜ ์†์ƒ ์—†์ด Eager/Lazy field๋ฅผ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถ”๊ฐ€๋กœ Tearcher๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” query๋„ ์•„๋ž˜์™€ ๊ฐ™์ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค

@EntityGraph(attributePaths = {"subjects", "subjects.teacher"})
@Query("SELECT a FROM School a")

์ฃผ์˜ํ•  ์ 

JoinFetch๋Š” Inner Join, Entity Graph๋Š” Outer Join ๋ผ๋Š” ์ฐจ์ด์ ์ด ์žˆ์Œ์„ ์œ ์˜ํ•˜์ž. ๊ณตํ†ต์ ์œผ๋กœ ์นดํ…Œ์‹œ์•ˆ ๊ณฑ(Cartesian Product) ์ด ๋ฐœ์ƒํ•˜์—ฌ, Subject ์ˆ˜ ๋งŒํผ School์ด ์ค‘๋ณต ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

Solution 1

1:N field์˜ type์„ Set์œผ๋กœ ์„ ์–ธํ•˜๊ธฐ Set์€ ์ค‘๋ณต์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ์ž๋ฃŒ ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ค‘๋ณต ๋“ฑ๋ก์ด ๋˜์ง€ ์•Š๋Š”๋‹ค

ex)

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name="school_id")
    private Set<Subject> subjects = new LinkedHashSet<>();

Set์€ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, LinkedHashSet์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•œ๋‹ค

Solution 2

distinct๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ธฐ

์ด ๋ถ€๋ถ„์€ @Query์—์„œ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, join fetch, @EntityGraph๋Š” ๋™์ผํ•˜๋‹ค

ex)

@Query("select DISTINCT a from School a join fetch a.subjects s join fetch s.teacher")
List<Academy> findAllWithTeacher();
@EntityGraph(attributePaths = {"subjects", "subjects.teacher"})
@Query("select DISTINCT a from School a")
List<Academy> findAllEntityGraphWithTeacher();

Last updated

Was this helpful?