TIL-066 | N+1 Query Problem

Lee, Chankyuยท2022๋…„ 1์›” 13์ผ
0

Computer Science

๋ชฉ๋ก ๋ณด๊ธฐ
6/12
post-thumbnail

๐ŸŒˆ N+1 Query Problem

N+1 Problem ์ด๋ž€?

  • ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด์Šˆ๋กœ ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๊ฒฝ์šฐ์— ์กฐํšŒ๋œ ๋ฐ์ดํ„ฐ ๊ฐฏ์ˆ˜(n) ๋งŒํผ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๊ฒŒ ๋œ๋‹ค. ์ด๋ฅผ N+1 ๋ฌธ์ œ๋ผ๊ณ  ํ•œ๋‹ค.
    ๐Ÿ‘‰ ์‰ฝ๊ฒŒ ๋งํ•ด์„œ, ์ฟผ๋ฆฌ 1๋ฒˆ์œผ๋กœ N๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๋Š”๋ฐ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์ด N๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ ์ˆ˜ ๋งŒํผ ๋ฐ˜๋ณตํ•ด์„œ 2์ฐจ์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฌธ์ œ๋‹ค.

โœ” Django์—์„œ์˜ N+1 Problem

django ORM์€ Lazy-Loading ๋ฐฉ์‹์ด๋‹ค.

  • Lazy-Loading ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ORM์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ชจ๋“  ๋ช…๋ น ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜๊ณ  ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•  ์‹œ์ ์ด ์™”์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

  • ์ด ๋ฌธ์ œ์˜ ํ•ด๊ฒฐ ๋ฐฉ์‹์œผ๋กœ Eager-Loading ๋ฐฉ์‹์ด ์žˆ๋‹ค. Eager-Loading๋ฐฉ์‹์€ ์‚ฌ์ „์— ์“ธ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์š”์ฒญํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„ํšจ์œจ์ ์œผ๋กœ ๋Š˜์–ด๋‚˜๋Š” ์ฟผ๋ฆฌ ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

โœ” Django์—์„œ Eager-Loading ๊ตฌํ˜„์„ ์œ„ํ•œ ๊ธฐ๋ฒ•

๐Ÿ˜ƒ select_related

  • ์ฐธ์กฐํ•˜๋Š” ๋Œ€์ƒ์ด ์ค‘๊ฐ„ํ…Œ์ด๋ธ”์ด ์•„๋‹ ์‹œ, Join ์ฟผ๋ฆฌ๋ฌธ์„ ์ด์šฉํ•ด์„œ data๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

  • ์ฆ‰, ๋‘ ํ…Œ์ด๋ธ”๊ฐ„ join์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
    join์„ ํ•ด์„œ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ฟผ๋ฆฌ๊ฐ€ 1๋ฒˆ ์š”์ฒญ ๋œ๋‹ค.

  • ๋”ฐ๋ผ์„œ ์ค‘๊ฐ„ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•ด์„œ ๊ด€๊ณ„๋ฅผ ํ˜•์„ฑํ•˜๋Š”, many-to-many ๋ชจ๋ธ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ , foreign-key , one-to-one๊ณผ ๊ฐ™์€ single-valued relationships์—์„œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿ˜ƒ prefetch_related

  • 2๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ๊ฐ๊ฐ ํ˜ธ์ถœํ•˜์—ฌ, django๋‚ด์—์„œ ๋ณ‘ํ•ฉํ•œ๋‹ค.

  • select_related์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” many-to-many๋ชจ๋ธ์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • foreign-key , one-to-one, ๊ทธ๋ฆฌ๊ณ  one-to-many ๋“ฑ์˜ ๋ชจ๋“  relationships์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • SQL์˜ WHERE โ€ฆ IN ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.


๐Ÿ™†โ€โ™‚๏ธ ์‚ฌ์šฉ ์˜ˆ์‹œ

        menus = Menu.objects.all()

        menu_data = [{
            'menu_id'   : menu.id,
            'menu_name' : menu.name,
            'image_url' : menu.image_url,
            'categories'  : [{
                'id'   : category.id,
                'name' : category.name,
                'subcategories'   : [{
                    'id'   : subcategory.id,
                    'name' : subcategory.name,
                } for subcategory in category.subcategory_set.all()],
            } for category in menu.category_set.all()],
        } for menu in menus]
  • ๋ฉ”๋‰ด -> ์นดํ…Œ๊ณ ๋ฆฌ -> ์„œ๋ธŒ์นดํ…Œ๊ณ ๋ฆฌ ์ˆœ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค. ๊ฐ๊ฐ์˜ ํ…Œ์ด๋ธ”์€ one-to-many ๊ด€๊ณ„๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.

  • ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฟผ๋ฆฌ ์š”์ฒญ ํšŸ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๋Š” ๊ตฌ์กฐ์ด๋‹ค.

โ— ์ฟผ๋ฆฌ ์š”์ฒญ ํšŸ์ˆ˜ : 20ํšŒ

        menus = Menu.objects.prefetch_related('category_set', 'category_set__subcategory_set')

        menu_data = [{
            'menu_id'   : menu.id,
            'menu_name' : menu.name,
            'image_url' : menu.image_url,
            'categories'  : [{
                'id'   : category.id,
                'name' : category.name,
                'subcategories'   : [{
                    'id'   : subcategory.id,
                    'name' : subcategory.name,
                } for subcategory in category.subcategory_set.all()],
            } for category in menu.category_set.all()],
        } for menu in menus]
  • ์œ„์˜ ์ฝ”๋“œ์—์„œ prefetch_related()๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์นดํ…Œ๊ณ ๋ฆฌ์™€ ์„œ๋ธŒ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์บ์‹ฑํ•˜์—ฌ ์ฟผ๋ฆฌ ์š”์ฒญ ํšŸ์ˆ˜๋ฅผ ๋“œ๋ผ๋งˆํ‹ฑํ•˜๊ฒŒ ๊ฐ์†Œ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

โ— ์ฟผ๋ฆฌ ์š”์ฒญ ํšŸ์ˆ˜ : 3ํšŒ


๐Ÿ“ Reference

  1. https://hckcksrl.medium.com/django-n-1-problem-d986b93f5d3e
  2. https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1
  3. https://velog.io/@burnkim61/Django-ORM-N1-%EB%AC%B8%EC%A0%9C
profile
Backend Developer - "Growth itself contains the germ of happiness"

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด