์ฟผ๋ฆฌ ํŠœ๋‹์„ ํ†ตํ•ด ํ”ผ๋“œ ๊ฐœ์„ ํ•˜๊ธฐ

doohyun.lim
  • #์ฟผ๋ฆฌ ํŠœ๋‹
  • #์„ฑ๋Šฅ ๊ฐœ์„ 
  • #typeORM
  • #DB
  • #sql

๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š”, ๋…ธ๋จธ์Šค ๋ฐฑ์—”๋“œํŒ€์— ํ•ฉ๋ฅ˜ํ•œ ์ž„๋‘ํ˜„์ž…๋‹ˆ๋‹ค.

๋…ธ๋จธ์Šค๋Š” fromm์ด๋ผ๋Š” ์†Œํ†ต ์•ฑ์„ ์„œ๋น„์Šคํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘ ์ฑ„๋„์ด๋ผ๋Š” ์„œ๋น„์Šค๊ฐ€ ์žˆ๋Š”๋ฐ, ์ฑ„๋„์€ ์•„ํ‹ฐ์ŠคํŠธ์™€ ํŒฌ๋“ค์ด ๊ฒŒ์‹œ๊ธ€๊ณผ ๋Œ“๊ธ€์„ ํ†ตํ•ด ์†Œํ†ตํ•˜๋ฉฐ, ๋ผ์ด๋ธŒ, ๊ณต์ง€์‚ฌํ•ญ, ๋‹ค์‹œ๋ณด๊ธฐ ๋“ฑ ํ•ด๋‹น ์•„ํ‹ฐ์ŠคํŠธ๊ฐ€ ์†Œ์†๋œ ๊ทธ๋ฃน์˜ ์ •๋ณด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ๋ฏธ์Šค๋‚˜์ธ ์ฑ„๋„

์ฑ„๋„(์ €์ž‘๊ถŒ์„ ์œ„ํ•ด ๋ธ”๋ผ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค)

์ฒซ ์—…๋ฌด๋กœ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ฑ„๋„์—์„œ ๋†’์€ ๋ถ€ํ•˜์™€ latency๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ–ˆ๊ณ , ์ด์— ๋ฌธ์ œ์˜ ์›์ธ์„ ๋ถ„์„ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ๋ถ„์„

RDS์—๋Š” ์„ฑ๋Šฅ ๊ฐœ์„  ๋„์šฐ๋ฏธ๋ผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์–ด ๋ถ€ํ•˜๋ฅผ ๋งŽ์ด ์ฐจ์ง€ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ๋ถ„์„ํ•œ ๊ฒฐ๊ณผ, ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ฟผ๋ฆฌ๋Š” ํŒฌ ํ”ผ๋“œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฟผ๋ฆฌ์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์ด ์ถ”๊ฐ€๋˜๋ฉด์„œ ํŒฌ ํ”ผ๋“œ ์กฐํšŒ ์ฟผ๋ฆฌ์— ์—ฌ๋Ÿฌ ๊ด€๊ณ„(relation)๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํŒฌ ํ”ผ๋“œ

์ฑ„๋„ ํŒฌํ”ผ๋“œ(์ €์ž‘๊ถŒ์„ ์œ„ํ•ด ๋ธ”๋ผ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค)

๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ๊ธฐ์กด ๊ธฐํš์— ์—†๋˜ ์š”๊ตฌ์‚ฌํ•ญ์ด๋‚˜ ์ถ”๊ฐ€ ๊ธฐํš์ด ๋งž๋ฌผ๋ ค ๊ธฐ์กด API์˜ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ๊ธฐ์กด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ฟผ๋ฆฌ ํŠœ๋‹์„ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

1๋‹จ๊ณ„: ์ธ๋ฑ์Šค ๊ฒ€์ฆ

์ฒซ ๋ฒˆ์งธ๋กœ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํŠœ๋‹ ๋ฐฉ๋ฒ•์€ ์ธ๋ฑ์Šค ํ™œ์šฉ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์–ด๋–ค ์ธ๋ฑ์Šค๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํŒŒ์•…ํ•ด์•ผ ์ฟผ๋ฆฌ์— ๋งž์ถฐ ์ตœ์ ํ™”๊ฐ€ ๋˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ธ๋ฑ์Šค ํ™•์ธ์€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ธ๋ฑ์Šค๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์žˆ์Œ์—๋„ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์— ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆฐ๋‹ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋ถ„ํ• ํ•˜๊ฑฐ๋‚˜ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜๋Š” ์กฐ์น˜๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, EXPLAIN ANALYZE ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ์˜ ์‹คํ–‰ ๊ณ„ํš์„ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด ์ธ๋ฑ์Šค ๊ฒ€์ฆ์˜ ์ฒซ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

EXPLAIN ANALYZE ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค

  • ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์ด ๋А๋ ค์ง„ ์ด์œ ๋ฅผ ์ฐพ์„ ๋•Œ
  • ์ธ๋ฑ์Šค๊ฐ€ ์ œ๋Œ€๋กœ ํ™œ์šฉ๋˜๋Š”์ง€ ํ™•์ธํ•  ๋•Œ
  • JOIN ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ์ง€ ํŒ๋‹จํ•  ๋•Œ

EXPLAIN ANALYZE๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•œ ๊ฒฐ๊ณผ, ์ ์ ˆํ•œ ์ธ๋ฑ์Šค๋ฅผ ํ™œ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ด๋ธ”์˜ ์ธ๋ฑ์Šค๋ฅผ ํ™•์ธํ•œ ๊ฒฐ๊ณผ ์ ์ ˆํ•œ ์ธ๋ฑ์Šค๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ–ˆ๊ณ , ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•œ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ์„  ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  1. ์ตœ์ ํ™”๋œ ์ธ๋ฑ์Šค ์ถ”๊ฐ€
  2. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํ™•์ธ
  3. ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๊ฐœ์„ 
  4. ๋ถ€ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค ๊ตฌ์กฐ ์ฒดํฌ

2๋‹จ๊ณ„: ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๋ถ„์„

ํ•ด๋‹น ์ฟผ๋ฆฌ๊ฐ€ ์œ„์น˜ํ•œ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•œ ๊ฒฐ๊ณผ, TypeORM์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฑ์—”๋“œ์—์„œ ๋‹จ์ˆœ ํ™•์ธ ๋กœ์ง์ด JOIN์ ˆ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ž์„ธํžˆ ์กฐ์‚ฌํ•ด๋ณด๋‹ˆ, ์ฒ˜์Œ์—๋Š” ์š”์ฒญํ•œ ๊ฒŒ์‹œ๋ฌผ์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์œ ์ €์˜ ์‹ ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ LEFT JOINํ•ด์„œ reportedPost.id IS NULL ์กฐ๊ฑด์„ ๊ฑฐ๋Š” ๋ถ€๋ถ„์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์ด ์„ฑ๋Šฅ ๋ถ€ํ•˜์˜ ์ฃผ์š” ์›์ธ์œผ๋กœ ์ถ”์ธก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

-- ๊ธฐ์กด ์ฟผ๋ฆฌ (๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๋ถ€๋ถ„)
SELECT feedPost.*
FROM feed_posts feedPost
LEFT JOIN reported_feed_posts reportedPost
  ON feedPost.id = reportedPost.post_id
  AND reportedPost.user_id = ?
  AND reportedPost.active = true
WHERE feedPost.channel_id = ?
  AND feedPost.active = true
  AND reportedPost.id IS NULL  -- ์ด ๋ถ€๋ถ„์ด ์„ฑ๋Šฅ ์ด์Šˆ ๋ฐœ์ƒ
ORDER BY feedPost.created_at DESC

์ฟผ๋ฆฌ ํŠœ๋‹ ์›์น™

์ฟผ๋ฆฌ ํŠœ๋‹์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ ์›์น™๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ์ขŒ๋ณ€ ๊ฐ€๊ณต ๊ธˆ์ง€: WHERE์ ˆ์—์„œ ์ปฌ๋Ÿผ์„ ํ•จ์ˆ˜๋กœ ๊ฐ€๊ณตํ•˜์ง€ ์•Š๊ธฐ
  2. ์ ์ ˆํ•œ ์กฐ์ธ ๋ฐฉ์‹ ์„ ํƒ: ๋‹จ์ˆœ ํ™•์ธ์šฉ์ด๋ผ๋ฉด JOIN < IN < EXISTS ์ˆœ์œผ๋กœ ์„ฑ๋Šฅ ์œ ๋ฆฌ
  3. NULL ์ฒ˜๋ฆฌ: NULL ๊ฐ’์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ
  4. ์กฐ์ธ ์ˆœ์„œ ์ตœ์ ํ™”: INNER JOIN ์‹œ ์ž‘์€ ํ…Œ์ด๋ธ”๋ถ€ํ„ฐ ์—ฐ์‚ฐ
  5. ๋ช…์‹œ์  Alias ์ž‘์„ฑ: ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•˜๊ณ  ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
  6. UNION ํ™œ์šฉ: ๋ณต์žกํ•œ WHERE์ ˆ ๋Œ€์‹  UNION ์‚ฌ์šฉ ๊ณ ๋ ค
  7. VIEW ํ™œ์šฉ: ๋ณต์žกํ•œ ์—ฐ์‚ฐ์˜ ๊ฒฝ์šฐ VIEW๋ฅผ ํ†ตํ•œ ๋‹จ์ˆœํ™”

์™œ 2๋ฒˆ ์›์น™์„ ์ ์šฉํ–ˆ๋Š”๊ฐ€?

๊ธฐ์กด ์ฟผ๋ฆฌ์—์„œ LEFT JOIN + IS NULL ํŒจํ„ด์€ **โ€œ์‹ ๊ณ ๋˜์ง€ ์•Š์€ ๊ฒŒ์‹œ๋ฌผ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐโ€**๋ผ๋Š” ๋‹จ์ˆœํ•œ ํ™•์ธ ๋กœ์ง์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ JOIN๋ณด๋‹ค๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค:

  • JOIN ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ : ๋ชจ๋“  ๊ฒŒ์‹œ๋ฌผ์— ๋Œ€ํ•ด ์‹ ๊ณ  ํ…Œ์ด๋ธ”๊ณผ ์กฐ์ธ ํ›„ NULL ์ฒดํฌ
  • ์„œ๋ธŒ์ฟผ๋ฆฌ ๋ฐฉ์‹์˜ ์žฅ์ : ์‹ ๊ณ ๋œ ๊ฒŒ์‹œ๋ฌผ ID๋งŒ ๋จผ์ € ์ถ”์ถœ ํ›„ ์ œ์™ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋” ์ง๊ด€์ ์ด๊ณ  ๋น ๋ฆ„

๋”ฐ๋ผ์„œ 2๋ฒˆ ์›์น™์— ๋”ฐ๋ผ JOIN์ ˆ์„ NOT IN ์„œ๋ธŒ์ฟผ๋ฆฌ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ

์›๋ž˜๋Š” EXISTS์ ˆ์„ ์‚ฌ์šฉํ•˜๋ ค ํ–ˆ์œผ๋‚˜, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์˜ ์ œ์•ฝ์œผ๋กœ NOT IN์ ˆ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • EXISTS: NULL ๊ฐ’ ์ฒ˜๋ฆฌ์— ์•ˆ์ „ํ•˜๊ณ , ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ๋” ๋น ๋ฆ„
  • NOT IN: NULL ๊ฐ’์ด ์žˆ์œผ๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ ๋ฐœ์ƒ ๊ฐ€๋Šฅ, ํ•˜์ง€๋งŒ ์ž‘์€ ๋ฐ์ดํ„ฐ์…‹์—์„œ๋Š” ์ถฉ๋ถ„ํžˆ ํšจ์œจ์ 

์ œ์•ฝ์‚ฌํ•ญ๊ณผ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„

๋ฐฑ์—”๋“œ ํŒ€์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ค‘์‹œํ•˜๋ฉฐ ๋ชจ๋“  ์„œ๋น„์Šค์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ pg-mem์„ ํ†ตํ•ด ๊ฐ€๋ณ๊ณ  ๋น ๋ฅธ ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, pg-mem์˜ ํ•œ๊ณ„๋กœ EXISTS์ ˆ์„ ํ•ด์„ํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋กœ ์ธํ•ด ๋‘ ๊ฐ€์ง€ ์„ ํƒ์ง€๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค:

  1. ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  EXISTS์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ ํ™”
  2. ์„ฑ๋Šฅ์ƒ ์•ฝ๊ฐ„์˜ ์†ํ•ด๋ฅผ ๊ฐ์ˆ˜ํ•˜๊ณ  NOT IN์ ˆ๋กœ ๊ฐœ์„ 

์˜์‚ฌ๊ฒฐ์ • ๊ณผ์ •

๊ฐœ๋ฐœ์—์„œ ์ค‘์š”ํ•œ ๊ฒƒ์€ ๊ธฐ์ˆ ์  ์™„๋ฒฝํ•จ๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ํšจ์œจ์„ฑ ์‚ฌ์ด์˜ ๊ท ํ˜•์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์š”๊ตฌ์‚ฌํ•ญ๋งŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ•ด๋‹น ๊ธฐ๋Šฅ์˜ ์—ฃ์ง€ ์ผ€์ด์Šค์™€ ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ์„ฑ์„ ๋น„์šฉ ์ธก๋ฉด์—์„œ ํšจ์œจ์ ์œผ๋กœ ํŒ๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

NOT IN์ ˆ ๋ณ€๊ฒฝ๊ณผ ์ธ๋ฑ์Šค ์ถ”๊ฐ€์— ๋น„ํ•ด ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์€ ์ƒ๋‹นํ•œ ๊ณต์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, NOT IN์ ˆ๊ณผ ์ธ๋ฑ์Šค ์ถ”๊ฐ€ ๋ฐฉ์‹์œผ๋กœ ๊ฐœ์„ ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๊ณผ์ •

๋ชฉ์ ์ด ์ •ํ•ด์ง€๋ฉด ๊ฐœ์„  ๊ณผ์ •์€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค:

  1. ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๊ฐœ์„ 
  2. ๊ฐœ์„ ๋œ ์ฟผ๋ฆฌ์— ๋งž๋Š” ์ธ๋ฑ์Šค ์ถ”๊ฐ€

๋จผ์ € ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ , ๊ฐœ์„ ๋œ ์ฟผ๋ฆฌ์— ๋งž์ถฐ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ˆœ์„œ๋กœ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๊ธฐ์กด ์ธ๋ฑ์Šค ํ˜„ํ™ฉ์„ ํŒŒ์•…ํ•˜๊ณ , ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ• ์ง€ ๊ณ„ํš์„ ์„ธ์› ์Šต๋‹ˆ๋‹ค.

์„œ๋ธŒ์ฟผ๋ฆฌ ํ™œ์šฉ ๋ฐฉ์‹

NOT IN์ ˆ์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ํ•„ํ„ฐ๋งํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ์ถ”์ถœ
  • ๋ฉ”์ธ ์ฟผ๋ฆฌ์˜ SELECT๋ฅผ ๋‹จ์ˆœํ™”ํ•˜์—ฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ

๊ฐœ์„  ์ „ ์ฟผ๋ฆฌ (JOIN ๋ฐฉ์‹)

// ๊ธฐ์กด JOIN ๋ฐฉ์‹ - ์„ฑ๋Šฅ ์ด์Šˆ ๋ฐœ์ƒ
const feedPosts = await this.dataSource
  .getRepository(FeedPost)
  .createQueryBuilder("feedPost")
  .leftJoin(
    "feedPost.reportedPosts",
    "reportedPost",
    "reportedPost.user_id = :userId AND reportedPost.active = :active"
  )
  .where("feedPost.channel_id = :channelId")
  .andWhere("feedPost.active = :active")
  .andWhere("reportedPost.id IS NULL") // ์‹ ๊ณ ๋˜์ง€ ์•Š์€ ๊ฒŒ์‹œ๋ฌผ๋งŒ
  .orderBy("feedPost.created_at", "DESC")
  .limit(20)
  .getMany();

๊ฐœ์„  ํ›„ ์ฟผ๋ฆฌ (์„œ๋ธŒ์ฟผ๋ฆฌ ๋ฐฉ์‹)

// ๊ฐœ์„ ๋œ NOT IN ์„œ๋ธŒ์ฟผ๋ฆฌ ๋ฐฉ์‹
const reportedPostIdsSubQuery = this.dataSource
  .getRepository(ReportedFeedPost)
  .createQueryBuilder("reportedFeedPost")
  .select("reportedFeedPost.post_id")
  .where(`reportedFeedPost.user_id = :userId`)
  .andWhere("reportedFeedPost.active = :active");

const feedPosts = await this.dataSource
  .getRepository(FeedPost)
  .createQueryBuilder("feedPost")
  .where("feedPost.channel_id = :channelId")
  .andWhere("feedPost.active = :active")
  .andWhere(`feedPost.id NOT IN (${reportedPostIdsSubQuery.getQuery()})`)
  .orderBy("feedPost.created_at", "DESC")
  .limit(20)
  .setParameters({ channelId, userId, active: true })
  .getMany();

์ฐธ๊ณ : NULL ๊ฐ’ ์ฒ˜๋ฆฌ

// ์ผ๋ฐ˜์ ์œผ๋กœ NOT IN ์‚ฌ์šฉ ์‹œ NULL ์•ˆ์ „์„ฑ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์กฐ๊ฑด์ด ํ•„์š”ํ•˜์ง€๋งŒ
// ์ด ๊ฒฝ์šฐ post_id๊ฐ€ FeedPost.id๋ฅผ ์ฐธ์กฐํ•˜๋Š” FK์ด๋ฏ€๋กœ NULL ๊ฐ’์ด ์—†์–ด ์ƒ๋žต ๊ฐ€๋Šฅ
.andWhere(`feedPost.id NOT IN (${reportedPostIdsSubQuery.getQuery()})`)

// ๋งŒ์•ฝ FK ์ œ์•ฝ์ด ์—†๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด NULL ์ฒ˜๋ฆฌ ํ•„์š”:
// .andWhere(`feedPost.id NOT IN (${reportedPostIdsSubQuery.getQuery()})
//            OR (${reportedPostIdsSubQuery.getQuery()}) IS NULL`)

์ธ๋ฑ์Šค ์ตœ์ ํ™”

๊ฐœ์„ ๋œ ์ฟผ๋ฆฌ์— ๋งž์ถฐ ๋‹ค์Œ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค:

์ถ”๊ฐ€๋œ ์ธ๋ฑ์Šค

-- FeedPost ํ…Œ์ด๋ธ” ๋ณตํ•ฉ ์ธ๋ฑ์Šค
CREATE INDEX CONCURRENTLY idx_feedpost_channel_active_created
ON feed_posts (channel_id, active, created_at DESC);

์ธ๋ฑ์Šค ์„ค๊ณ„ ๊ณ ๋ ค์‚ฌํ•ญ

  • CONCURRENTLY ์˜ต์…˜: ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์ธ๋ฑ์Šค ์ƒ์„ฑ
  • ์ปฌ๋Ÿผ ์ˆœ์„œ: WHERE์ ˆ์—์„œ ๊ฐ€์žฅ ์„ ํƒ์ ์ธ ์ปฌ๋Ÿผ์„ ์•ž์— ๋ฐฐ์น˜
  • DESC ์ •๋ ฌ: ์ตœ์‹  ๊ฒŒ์‹œ๋ฌผ ์กฐํšŒ ํŒจํ„ด์— ๋งž์ถฐ created_at์„ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์„ค์ •

์„ฑ๋Šฅ ๊ฐœ์„  ๊ฒฐ๊ณผ

์‘๋‹ต ์‹œ๊ฐ„ ๊ฐœ์„ 

๊ฐœ์„  ์ „

๊ฐœ์„  ์ „ ํŒฌํ”ผ๋“œ(์ €์ž‘๊ถŒ์„ ์œ„ํ•ด ๋ธ”๋ผ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค)

๊ฐœ์„  ํ›„

๊ฐœ์„  ํ›„ ํŒฌํ”ผ๋“œ(์ €์ž‘๊ถŒ์„ ์œ„ํ•ด ๋ธ”๋ผ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ถ€ํ•˜ ๊ฐœ์„ 

RDS ๋ถ€ํ•˜ ๊ฐœ์„ 

RDS CPU ๋ถ€ํ•˜

์‹ค์ธก ์„ฑ๋Šฅ ๊ฐœ์„  ๊ฒฐ๊ณผ

์ฟผ๋ฆฌ ๊ตฌ์กฐ ๊ฐœ์„ ๊ณผ ์ธ๋ฑ์Šค ์ถ”๊ฐ€๋ฅผ ํ†ตํ•ด ์‹ค์ œ ์ธก์ •๋œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ์‘๋‹ต ์‹œ๊ฐ„: ํ‰๊ท  6.3์ดˆ โ†’ 0.4์ดˆ (์•ฝ 94% ๊ฐœ์„ )
  • CPU ์‚ฌ์šฉ๋ฅ : ํ‰๊ท  75% โ†’ 5% (์•ฝ 93% ๊ฐ์†Œ)

์ด๋Ÿฌํ•œ ๊ฐœ์„ ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋“ค์ด ํ”ผ๋“œ๋ฅผ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ , ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰๋„ ํฌ๊ฒŒ ์ค„์–ด๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์„  ์ „ํ›„ ๋น„๊ต ์˜์ƒ๊ณผ RDS ๋ถ€ํ•˜ ๊ทธ๋ž˜ํ”„์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ์ด ์ƒ๋‹นํ•œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฝํ—˜์„ ํ†ตํ•ด ์–ป์€ ๊ตํ›ˆ

์ด๋ฒˆ ์ฟผ๋ฆฌ ํŠœ๋‹ ์ž‘์—…์„ ํ†ตํ•ด ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค:

  1. ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง์˜ ์ค‘์š”์„ฑ: RDS ์„ฑ๋Šฅ ๊ฐœ์„  ๋„์šฐ๋ฏธ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•œ ์ง€์†์ ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๋Š” ํ•ต์‹ฌ
  2. ํ˜„์‹ค์  ์ ‘๊ทผ์˜ ๊ฐ€์น˜: ๊ธฐ์ˆ ์  ์™„๋ฒฝํ•จ๋ณด๋‹ค๋Š” ํ˜„์‹ค์ ์ด๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๊ฐœ์„ ์ด ๋” ํฐ ๊ฐ€์น˜๋ฅผ ์ฐฝ์ถœ
  3. ์ฒด๊ณ„์  ๋ฌธ์ œ ํ•ด๊ฒฐ: ์ธ๋ฑ์Šค ๊ฒ€์ฆ โ†’ ์ฟผ๋ฆฌ ๋ถ„์„ โ†’ ๊ตฌ์กฐ ๊ฐœ์„  โ†’ ์„ฑ๋Šฅ ์ธก์ •์˜ ๋‹จ๊ณ„์  ์ ‘๊ทผ์ด ํšจ๊ณผ์ 
  4. ์ œ์•ฝ์‚ฌํ•ญ ์ธ์ •: ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์˜ ํ•œ๊ณ„ ๊ฐ™์€ ์‹ค๋ฌด์  ์ œ์•ฝ์„ ์ธ์ •ํ•˜๊ณ  ์ตœ์„ ์˜ ๋Œ€์•ˆ ์„ ํƒ

์ฟผ๋ฆฌ ํŠœ๋‹ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

์ธ๋ฑ์Šค ์ถ”๊ฐ€ ์‹œ ๊ณ ๋ คํ•  ์ 

์ธ๋ฑ์Šค๋Š” ์กฐํšŒ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์“ฐ๊ธฐ ์„ฑ๋Šฅ ์˜ํ–ฅ: ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ์ˆ˜์ •๋  ๋•Œ๋งˆ๋‹ค ์ธ๋ฑ์Šค๋„ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ INSERT/UPDATE ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉ๋Ÿ‰: ๋ณตํ•ฉ ์ธ๋ฑ์Šค๋Š” ์ถ”๊ฐ€์ ์ธ ๋””์Šคํฌ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•˜๋ฏ€๋กœ ์Šคํ† ๋ฆฌ์ง€ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
  • ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ: ์ธ๋ฑ์Šค๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ๊ด€๋ฆฌ ํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ , ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค

NOT IN vs EXISTS ์„ ํƒ ๊ธฐ์ค€

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ EXISTS ๋Œ€์‹  NOT IN์„ ์„ ํƒํ–ˆ์ง€๋งŒ, ์ผ๋ฐ˜์ ์ธ ์„ ํƒ ๊ธฐ์ค€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

-- ์ž‘์€ ์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์…‹ (< 1000๊ฑด): NOT IN ์‚ฌ์šฉ ๊ฐ€๋Šฅ
-- ํฐ ์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์…‹ (> 1000๊ฑด): EXISTS ๊ถŒ์žฅ
-- NULL ๊ฐ’ ๊ฐ€๋Šฅ์„ฑ: ํ•ญ์ƒ EXISTS ์‚ฌ์šฉ ๊ถŒ์žฅ

ํŠนํžˆ NOT IN์€ ์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์— NULL์ด ํฌํ•จ๋˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด๋ฒˆ ๊ฒฝ์šฐ์ฒ˜๋Ÿผ FK ์ œ์•ฝ์กฐ๊ฑด์ด ์žˆ์–ด NULL ๊ฐ’์ด ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์—†์ด ์‚ฌ์šฉํ•ด๋„ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ํŠœ๋‹ ์ ‘๊ทผ ์ˆœ์„œ

๊ฒฝํ—˜์ƒ ๋‹ค์Œ ์ˆœ์„œ๋กœ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค:

  1. ์ธ๋ฑ์Šค ์ตœ์ ํ™”: ๊ฐ€์žฅ ์ ์€ ๋น„์šฉ์œผ๋กœ ํฐ ํšจ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ์ฟผ๋ฆฌ๋ฅผ ๊ฑฐ์˜ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
  2. ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๊ฐœ์„ : ์ค‘๊ฐ„ ์ •๋„์˜ ํšจ๊ณผ๋ฅผ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฝ”๋“œ ์ˆ˜์ •๊ณผ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค
  3. ์บ์‹ฑ ๋„์ž…: ๋†’์€ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต ๋“ฑ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
  4. ํ•˜๋“œ์›จ์–ด ์—…๊ทธ๋ ˆ์ด๋“œ: ์ฆ‰์‹œ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ ๋น„์šฉ์ด ๊ฐ€์žฅ ๋†’๊ณ , ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์ฑ…์€ ์•„๋‹™๋‹ˆ๋‹ค

๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ํ•˜๋‚˜์˜ API์— ๋‹ค์–‘ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ์ถ”๊ฐ€๋˜๋ฉด์„œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์ž์‹ ์ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ์— ๋Œ€ํ•ด ์ง€์†์ ์œผ๋กœ ๊ฒ€ํ† ํ•˜๊ณ  ๊ฐœ์„ ํ•ด ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

fromm ์‚ฌ์šฉ์ž๋“ค์ด ๋” ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์†์ ์ธ ์„ฑ๋Šฅ ๊ฐœ์„ ์— ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ