βπ[Backend Development] λκ·λͺ¨ λ°μ΄ν°μμ κ²μκΈ λͺ©λ‘ μ‘°νκ° λ³΅μ‘ν μ΄μ
π Intro
- λκ·λͺ¨ λ°μ΄ν°μμ κ²μκΈ λͺ©λ‘ μ‘°νκ° λ³΅μ‘ν μ΄μ λ μ¬λ¬ κ°μ§κ° μμ΅λλ€.
- μΌλ°μ μΌλ‘ μκ·λͺ¨ λ°μ΄ν°λ² μ΄μ€μμλ λ¨μν
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10
κ°μ μΏΌλ¦¬λ‘ μ½κ² ν΄κ²°ν μ μμ§λ§, μλ°±λ§~μμ΅ κ°μ λ°μ΄ν°λ₯Ό λ€λ€μΌ νλ μμ€ν μμλ μ¬λ¬ 볡μ‘ν λ¬Έμ κ° λ°μν©λλ€.
β 1οΈβ£ λκ·λͺ¨ λ°μ΄ν°μμ κ²μκΈ λͺ©λ‘ μ‘°νκ° λ³΅μ‘ν μ΄μ
1οΈβ£ λ°μ΄ν°μ μμ΄ λ°©λν¨
- κ²μκΈμ΄ λ§μμ§μλ‘ ORDER BYμ LIMIT μ°μ°μ λΆλ΄μ΄ 컀μ§
- μΈλ±μ€λ₯Ό μ¬μ©νλλΌλ λμ€ν¬μ I/O λΉμ©μ΄ μ¦κ°νκ³ μΊμ±μ΄ μ΄λ ΅κ² λ¨
π μμ 쿼리
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10;
β λ¬Έμ μ
- ORDER BY create_at DESC μ€ν μ λͺ¨λ κ²μκΈμ μ λ ¬ν΄μΌ ν¨ (λ°μ΄ν°κ° ν΄μλ‘ λλ €μ§)
- μ΅μ κ²μκΈμ΄ λ§μΌλ©΄ μλ‘μ΄ λ°μ΄ν°κ° κ³μ μΆκ°λλ©° μΈλ±μ€κ° μμ£Ό λ³κ²½λ¨
2οΈβ£ νμ΄μ§λ€μ΄μ (Pagination) μ±λ₯ μ ν
- κ²μνμ λ³΄ν΅ νμ΄μ§λ€μ΄μ μ μ§μν΄μΌ ν©λλ€.
- μ¦, LIMITκ³Ό OFFSETμ μ¬μ©ν΄ νΉμ νμ΄μ§μ κ²μκΈμ μ‘°νν΄μΌ νλλ°, λκ·λͺ¨ λ°μ΄ν°μμλ OFFSETμ΄ ν΄μλ‘ μ±λ₯μ΄ μ νλ©λλ€.
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10 OFFSET 1000000;
β λ¬Έμ μ
- OFFSET 1000000μ μ¬μ©νλ©΄ μμ 100λ§ κ° λ°μ΄ν°λ₯Ό μ€μΊνκ³ λ²λ¦° ν κ·Έ λ€μ 10κ°λ₯Ό λ°νν©λλ€.
- λ°μ΄ν°κ° λ§μμλ‘ λΆνμν μ°μ°μ΄ λ§μμ§κ³ μ±λ₯μ΄ κΈκ²©ν μ νλ©λλ€.
β ν΄κ²° λ°©λ²
- Keyset pagination (Seek λ°©μ) νμ© β OFFSET μμ΄ WHERE 쑰건μ νμ©
- LIMITμ νμ©ν΄ μ΄μ μ‘°νλ λ§μ§λ§ IDλ₯Ό κΈ°μ€μΌλ‘ λ€μ λ°μ΄ν°λ₯Ό κ°μ Έμ€κΈ°
SELECT * FROM posts WHERE created_at < '2024-03-11 10:00:00' ORDER BY created_at DESC LIMIT 10;
3οΈβ£ μΈλ±μ€ μ¬μ© μ΅μ ν λ¬Έμ
π μ μΈλ±μ€λ§μΌλ‘ ν΄κ²°λμ§ μμκΉ?
- μΈλ±μ€λ₯Ό μ¬μ©νλ©΄ μ λ ¬μ΄ λΉ¨λΌμ§μ§λ§, λ°μ΄ν°κ° λ§μ κ²½μ°μλ μ¬μ ν λμ€ν¬ I/O λΉμ©μ΄ μ¦κ°
- μλ‘μ΄ κ²μκΈμ΄ μΆκ°λ λλ§λ€ B-Tree μΈλ±μ€κ° κ°±μ λλ©° μ±λ₯μ λΆλ΄μ μ€
β ν΄κ²° λ°©λ²
- 컀λ²λ§ μΈλ±μ€ (Covering Index) νμ© β SELECTμ ν¬ν¨λ λͺ¨λ 컬λΌμ μΈλ±μ€μ 미리 ν¬ν¨
-
νν°μ
λ (Partitioning) β λ μ§λ³ ν
μ΄λΈ λΆλ¦¬ (posts_202403 κ°μ ν
μ΄λΈ)
CREATE INDEX idx_posts_created ON posts(created_at, title, content);
4οΈβ£ νΈλν½ λΆμ° λ¬Έμ
- κ²μκΈ λͺ©λ‘μ λλΆλΆ μλΉμ€μμ μ‘°ν νΈλν½μ΄ λ§κ³ , μ°κΈ° νΈλν½λ κΎΈμ€ν λ°μνλ ꡬ쑰μ λλ€.
- μ¦, μ½κΈ°(READ)κ° λ§μ§λ§, λμμ μλ‘μ΄ κ²μκΈμ΄ μΆκ°λλ©° μ λ ¬ μμκ° κ³μ λ°λλλ€.
β λ¬Έμ μ
- μΊμ μ¬μ©μ΄ μ΄λ ΅λ€ β μλ‘μ΄ κ²μκΈμ΄ μΆκ°λλ©΄ μ λ ¬ μμκ° λ°λμ΄ κΈ°μ‘΄ μΊμ 무ν¨νλ¨
- λμμ±μ΄ μ¦κ°νλ©΄ DB λΆνκ° μ»€μ§ β λ§μ μ μ κ° κ°μ λͺ©λ‘μ μμ²νλ©΄ DB λΆν μ¦κ°
β ν΄κ²° λ°©λ²
1οΈβ£ μΊμ± (Redis, Memcached) νμ©
- μ΅μ κ²μκΈ λͺ©λ‘μ Redisμ μ μ₯νκ³ , λ°μ΄ν°κ° λ³κ²½λ λλ§ μ
λ°μ΄νΈ.
redisTemplate.opsForValue().set("latest_posts", posts, Duration.ofSeconds(60));
2οΈβ£ μ½κΈ° μ μ© λ°μ΄ν°λ² μ΄μ€(Read Replica) νμ©
- Master-Slave Replicationμ μ¬μ©νμ¬ μ½κΈ° νΈλν½μ Slave DBλ‘ λΆμ°
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10; -- μ΄ μΏΌλ¦¬λ₯Ό Slave DBμμ μ€ννμ¬ Master DB λΆν κ°μ
3οΈβ£ CQRS ν¨ν΄ μ μ©
- κ²μκΈ μ‘°νμ μμ±/μμ μ λ€λ₯Έ DBλ‘ λΆλ¦¬ (μ½κΈ° μ μ© DB, μ°κΈ° μ μ© DB)
5οΈβ£ JOIN μ°μ° μ±λ₯ λ¬Έμ
- κ²μκΈ λͺ©λ‘μ μ‘°νν λ λ³΄ν΅ μμ±μ μ 보, μ’μμ μ, λκΈ μ λ±μ ν¨κ» μ‘°νν΄μΌ ν©λλ€.
- μ¦, JOINμ μνν΄μΌ νλλ°, λ°μ΄ν°κ° λ§μμλ‘ μ±λ₯μ΄ μ νλ©λλ€.
SELECT p.id, p.title, p.content, u.name, COUNT(c.id) as comment_count FROM posts p LEFT JOIN users u ON p.user_id = u.id LEFT JOIN comments c ON p.id = c.post_id GROUP BY p.id, u.name ORDER BY p.created_at DESC LIMIT 10;
β λ¬Έμ μ
- JOINμ μνν λ λ°μ΄ν°κ° λ§μΌλ©΄ λ©λͺ¨λ¦¬μ CPUκ° νμ
- GROUP BYλ₯Ό μννλ©΄ μ λ ¬ λ° μ§κ³ μ°μ°μ΄ νμνμ¬ μ±λ₯μ΄ λ λλ €μ§
β ν΄κ²° λ°©λ²
- NoSQL(μ: Redis, Elasticsearch) μΊμ νμ© β μ’μμ μ, λκΈ μλ 미리 μ μ₯ν΄λ
-
CQRS ν¨ν΄ μ μ© β λ³λ ν
μ΄λΈμ 미리 κ³μ°λ μΉ΄μ΄νΈ κ°μ μ μ₯
SELECT p.id, p.title, p.title, u.name, p.comment_count FROM posts p LEFT JOIN users u ON p.user_id = u.id ORDER BY p.created_at DESC LIMIT 10;
- p.comment_countλ 미리 κ³μ°λ κ°μ΄λ―λ‘ JOINμ μ€μ¬ μ±λ₯μ κ°μ ν μ μμ΅λλ€.
β 2οΈβ£ λκ·λͺ¨ κ²μκΈ μ‘°ν μ μ΅μ ν λ°©λ²
1οΈβ£ Keyset Pagination μ¬μ©
SELECT * FROM posts WHERE created_at < '2024-03-11 10:00:00' ORDER BY created_at DESC LIMIT 10;
- OFFSET μμ΄ μ΄μ μ‘°νλ λ§μ§λ§ IDλ₯Ό κΈ°μ€μΌλ‘ λ€μ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ λ°©μ
2οΈβ£ μΊμ± νμ© (Redis, Elasticsearch)
redisTemplate.opsForValue().set(CACHE_KEY, posts, Duration.ofSeconds(60))
- μ΅μ κ²μκΈμ μΊμμ μ μ₯νμ¬ DB λΆνλ₯Ό μ€μ
3οΈβ£ μ½κΈ° μ μ© DB(Read Replica) νμ©
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10;
- μ½κΈ° νΈλν½μ Replica DBλ‘ λΆμ°νμ¬ μ±λ₯ μ΅μ ν
4οΈβ£ 미리 κ³μ°λ κ° μ¬μ©
SELECT p.id, p.title, p.content, u.name, p.comment_count
FROM posts p
LEFT JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC LIMIT 10;
- λκΈ κ°μ, μ’μμ μ λ±μ 미리 κ³μ°λ κ°μΌλ‘ μ μ₯νμ¬ JOIN μ°μ° μ€μ΄κΈ°
β 3οΈβ£ κ²°λ‘
β λκ·λͺ¨ λ°μ΄ν°μμ κ²μκΈ λͺ©λ‘ μ‘°νκ° λ³΅μ‘ν μ΄μ λ?
- λ°μ΄ν° μμ΄ λ§μ ORDER BY LIMITκ° λΉν¨μ¨μ
- OFFSETμ΄ ν΄μλ‘ μ±λ₯ μ ν (νμ΄μ§ λ¬Έμ )
- JOIN μ°μ°μ΄ λ§μμλ‘ μ±λ₯ μ ν
- μ°κΈ° νΈλν½μ΄ λ§μμ§λ©΄ μΈλ±μ€ κ΄λ¦¬ λΆλ΄ μ¦κ°
- μΊμκ° μμ£Ό 무ν¨μλμ΄ DB λΆνκ° μ»€μ§
β
μ΅μ ν λ°©λ²
1οΈβ£ Keyset Pagination β OFFSET λμ λ§μ§λ§ ID κΈ°λ° μ‘°ν
2οΈβ£ Redis, Elasticsearch μΊμ± β μ΅μ λ°μ΄ν° 미리 μ μ₯
3οΈβ£ μ½κΈ° μ μ© DB(Read Replica) νμ© β μ‘°ν νΈλν½ λΆμ°
4οΈβ£ 미리 κ³μ°λ λ°μ΄ν° νμ© β JOIN μ΅μν