๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“• Spring Framework/Spring ๊ฐœ๋… ์ •๋ฆฌ

[Spring data JPA] N+1 ๋ฌธ์ œ ํ•ด๊ฒฐ

by GroovyArea 2023. 2. 28.

 

์ด๋ ‡๊ฒŒ ์ด๋ฃจ์–ด์ง„ ERD ๋ชจ๋ธ์ด ์žˆ๋‹ค. (์˜ˆ์‹œ)

 

์กฐ๊ฑด์— ๋งž์ถฐ ๋ถˆ๋Ÿฌ์˜ค๊ณ ์ž ํ•˜๋Š” Data๋Š” ์„ธ ํ…Œ์ด๋ธ”์˜ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ํ•„์š”๋กœ ํ•œ๋‹ค.

์ด๋•Œ Fetch join์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

Permission ์ž…์žฅ์—์„œ user์™€ document๋ฅผ ๋‘๋ฒˆ ๋‹ค fetch join ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๋Šฅ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์„ฑ๋Šฅ ์ƒ ์—„์ฒญ๋‚œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์„ ๊ฒฝ์šฐ, ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ์˜ ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚˜๋ˆ ์„œ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ผ ๊ฒƒ์ด๋‹ค.

 

ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๋“ค์€ ๋ชจ๋‘ FetchType.LAZY ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค.

 

๊ธฐ์กด ์ฟผ๋ฆฌ :

val documents = documentQueryService.findDocumentsByIdFetchJoinPermissions(projectId)
        
        return Response.UserPermission(
            documents.permissions.asSequence()
                .filter { it.status != STATUS.DELETED }
                .map { Response.UserPermission(it.user, it.permission) }
                .toList()
        )
  • document์˜ permissions ๋ฅผ fetch joinํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ง€๊ณ  it.user๋ฅผ ํ†ตํ•ด ์‹œํ€€์Šค๋ฅผ ๋Œ๋ฆฐ๋‹ค.
  • ์ด๋•Œ permission์˜ ๊ฐœ์ˆ˜๋งŒํผ user๋ฅผ select ํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๊ณ„์†์ ์œผ๋กœ ๋ฐœ์ƒํ•œ๋‹ค.
  • DB Connection ๋‚ญ๋น„ ๋ฐ ๋ถˆํ•„์š”ํ•œ Query ์˜ ๋ฐœ์ƒ

๊ธฐ์กด ์ฟผ๋ฆฌ์˜ API time

๊ฐœ์„  ์ฟผ๋ฆฌ :

val documents = documentQueryService.findDocumentsByIdFetchJoinPermissions(projectId)
val permissions = permissionQueryService.findPermissionsWithFetchJoinUsers(project.permissions)

        return Response.UserPermission(
            documents.permissions.asSequence()
                .filter { it.status != STATUS.DELETED }
                .map { Response.UserPermission(it.user, it.permission) }
                .toList()
        )
  • user ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฟผ๋ฆฌ๋ฅผ ์—†์• ๊ธฐ ์œ„ํ•ด user์™€ permission์„ fetchjoin ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒ์‹œ์ผฐ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด user ๋“ค์„ ๋”ฐ๋กœ selectํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ด๋Ÿฐ์‹์œผ๋กœ one to many ๊ด€๊ณ„์˜ fetch join์„ ๋™์‹œ์— ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋‚˜๋ˆ ์„œ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์œผ๋กœ ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

๊ฐœ์„ ๋œ API 2๋ฐฐ ์ด์ƒ ๊ฐ์†Œํ•œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

๊ณ ์ฐฐ

๋ฐฑ์—”๋“œ์˜ API ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ •๋‹ต์ด ์—†๋‹ค.

์ƒํ™ฉ์— ๋งž๊ฒŒ ํƒ€ํ˜‘์„ ๋ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„๋„ ๋‹น์—ฐํžˆ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค.

 

๋ณดํ†ต ๋ฐฑ์—”๋“œ์—์„œ ์„ฑ๋Šฅ์„ ๊ฐ€์žฅ ๋งŽ์ด ๊ฐœ์„ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์€ DB์™€์˜ ์ปค๋„ฅ์…˜์ด๋ฏ€๋กœ, JPA๋ฅผ ์ด์šฉํ•˜์—ฌ SQL์„ ์ž˜ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ์„ฑ๋Šฅ ๋ฌธ์ œ๋Š” ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

์‹ค์ œ๋กœ ํ™•์—ฐํ•˜๊ฒŒ ์ฐจ์ด๋‚œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ๊ทธ ์ด์ ์„ ํ™•์‹คํžˆ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

(user data๊ฐ€ 5๊ฐœ ๋ฐ–์— ์—†๋Š”๋ฐ๋„ ๋‘๋ฐฐ ์ด์ƒ ์ฐจ์ด๋‚œ ๊ฒƒ์€, ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์งˆ ๊ฒฝ์šฐ, ํ™•์‹คํ•˜๊ฒŒ ์„ฑ๋Šฅ ๊ฐœ์„ ์ด ์ด๋ฃจ์–ด์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.) 

 

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ๋” ํ™•์‹คํ•˜๊ฒŒ ๋ฐฑ์—”๋“œ์˜ ์„ฑ๋Šฅ์  ๊ณ ๋ฏผ๊ณผ ํ•ด๊ฒฐ์„ ์ž˜ ํ•ด๋‚˜๊ฐ€๋ฆฌ๋ผ ๋‹ค์งํ•ด๋ณธ๋‹ค.

๋ฐ˜์‘ํ˜•