Database

๐Ÿš€ Redis ์บ์‹ฑ ์ „๋žต: ์›น์‚ฌ์ดํŠธ ์†๋„ 10๋ฐฐ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“œ๋Š” ์‹ค์ „ ๊ฐ€์ด๋“œ

๊ด€๋ฆฌ์ž

2๊ฐœ์›” ์ „

14700
#Database#Performance#Redis#์บ์‹ฑ

๐Ÿš€ Redis ์บ์‹ฑ ์ „๋žต: ์›น์‚ฌ์ดํŠธ ์†๋„ 10๋ฐฐ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“œ๋Š” ์‹ค์ „ ๊ฐ€์ด๋“œ

๐Ÿค” ์›น์‚ฌ์ดํŠธ๊ฐ€ ๋А๋ ค์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋– ๋‚˜๊ฐ„๋‹ค๊ณ ์š”?

๋กœ๋”ฉ์ด 3์ดˆ๋งŒ ๋Šฆ์–ด์ ธ๋„ ์‚ฌ์šฉ์ž์˜ 40%๊ฐ€ ๋– ๋‚œ๋‹ค๋Š” ํ†ต๊ณ„, ๋“ค์–ด๋ณด์…จ์ฃ ? ์‹ค์ œ๋กœ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ๋•Œ๋ฌธ์— ๊ณ ์ƒํ•˜๊ณ  ์žˆ์–ด์š”.

์ด๋Ÿฐ ๊ฐ„๋‹จํ•œ ์ฟผ๋ฆฌ๋„ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ์•„์ง€๋ฉด ๋ช‡ ์ดˆ์”ฉ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ฑฐ๋“ ์š”. ๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ Redis ์บ์‹ฑ์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•œ ๋ฐฉ์— ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”!

โšก Redis๊ฐ€ ๋ญ๊ธธ๋ž˜ ์ด๋ ‡๊ฒŒ ๋น ๋ฅธ๊ฐ€์š”?

Redis๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” In-Memory ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ˆ์š”. ํ•˜๋“œ๋””์Šคํฌ๊ฐ€ ์•„๋‹ˆ๋ผ RAM์— ์ €์žฅํ•˜๋‹ˆ๊นŒ ์ ‘๊ทผ ์†๋„๊ฐ€ ๋ง๋„ ์•ˆ ๋˜๊ฒŒ ๋นจ๋ผ์š”.

์‹ค์ œ ์†๋„ ๋น„๊ต:

  • ํ•˜๋“œ๋””์Šคํฌ: 10ms (๋А๋ฆผ)
  • SSD: 0.1ms (๋น ๋ฆ„)
  • Redis (๋ฉ”๋ชจ๋ฆฌ): 0.01ms (์ดˆ๊ณ ์†!)

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ Redis๋ฅผ ์“ฐ๋ฉด 100๋ฐฐ~1000๋ฐฐ ๋น ๋ฅธ ๊ฑฐ์ฃ !

๐ŸŽฏ ์–ธ์ œ Redis ์บ์‹ฑ์„ ์จ์•ผ ํ• ๊นŒ์š”?

โœ… ์ด๋Ÿฐ ์ƒํ™ฉ์ด๋ผ๋ฉด Redis๊ฐ€ ๋‹ต์ด์—์š”

1. ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ž์ฃผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ

  • ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด
  • ์ƒํ’ˆ ๋ชฉ๋ก ๋ฐ์ดํ„ฐ
  • ๋ฉ”๋‰ด, ์นดํ…Œ๊ณ ๋ฆฌ ์ •๋ณด

2. ๋ณต์žกํ•œ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ

  • ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ (์˜ค๋Š˜์˜ ๋ฐฉ๋ฌธ์ž ์ˆ˜)
  • ์ธ๊ธฐ ๊ฒŒ์‹œ๊ธ€ ์ˆœ์œ„
  • ์ถ”์ฒœ ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ

3. ์„ธ์…˜ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

  • ๋กœ๊ทธ์ธ ์ƒํƒœ ์œ ์ง€
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ •๋ณด
  • ์ž„์‹œ ์ €์žฅ ๋ฐ์ดํ„ฐ

๐Ÿ› ๏ธ Redis ์„ค์น˜ํ•˜๊ณ  ์‹œ์ž‘ํ•˜๊ธฐ

Windows์—์„œ Redis ์„ค์น˜

# Chocolatey๋กœ ์„ค์น˜ (๊ฐ€์žฅ ์‰ฌ์›€)
choco install redis-64

# ๋˜๋Š” WSL ์‚ฌ์šฉ
wsl --install
sudo apt update
sudo apt install redis-server

macOS์—์„œ Redis ์„ค์น˜

# Homebrew๋กœ ์„ค์น˜
brew install redis

# Redis ์‹คํ–‰
brew services start redis

์„ค์น˜ ํ™•์ธ

redis-cli ping
# ์‘๋‹ต: PONG (์„ฑ๊ณต!)

๐Ÿ’ก ์‹ค์ „ ์บ์‹ฑ ์ „๋žต 3๊ฐ€์ง€

1๏ธโƒฃ Cache-Aside ํŒจํ„ด (๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ)

์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋ฉด:

  1. ์บ์‹œ์— ์žˆ๋‚˜ ํ™•์ธ โ†’ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ๋ฆฌํ„ด
  2. ์—†์œผ๋ฉด DB์—์„œ ์กฐํšŒ โ†’ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅ ํ›„ ๋ฆฌํ„ด
// Node.js ์˜ˆ์‹œ
async function getUser(userId) {
  // 1. Redis์—์„œ ๋จผ์ € ํ™•์ธ
  const cached = await redis.get(`user:${userId}`)
  if (cached) {
    console.log('์บ์‹œ์—์„œ ๊ฐ€์ ธ์˜ด! ์ดˆ๊ณ ์†!')
    return JSON.parse(cached)
  }
  
  // 2. ์บ์‹œ์— ์—†์œผ๋ฉด DB์—์„œ ์กฐํšŒ
  const user = await db.user.findById(userId)
  
  // 3. ๋‹ค์Œ๋ฒˆ์„ ์œ„ํ•ด ์บ์‹œ์— ์ €์žฅ (1์‹œ๊ฐ„ ์œ ์ง€)
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(user))
  
  console.log('DB์—์„œ ๊ฐ€์ ธ์™€์„œ ์บ์‹œ์— ์ €์žฅํ•จ')
  return user
}

2๏ธโƒฃ Write-Through ํŒจํ„ด (๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์ค‘์š”ํ•  ๋•Œ)

๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ DB์™€ ์บ์‹œ๋ฅผ ๋™์‹œ์— ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”.

async function updateUser(userId, userData) {
  // 1. DB ์—…๋ฐ์ดํŠธ
  const updatedUser = await db.user.update(userId, userData)
  
  // 2. ์บ์‹œ๋„ ๋™์‹œ์— ์—…๋ฐ์ดํŠธ
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(updatedUser))
  
  return updatedUser
}

3๏ธโƒฃ Write-Behind ํŒจํ„ด (๊ณ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ)

์บ์‹œ์—๋งŒ ๋จผ์ € ์ €์žฅํ•˜๊ณ , DB๋Š” ๋‚˜์ค‘์— ๋น„๋™๊ธฐ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”.

async function incrementViewCount(postId) {
  // 1. ์บ์‹œ์—์„œ ์ฆ‰์‹œ ์ฆ๊ฐ€ (์ดˆ๊ณ ์†!)
  await redis.incr(`post:views:${postId}`)
  
  // 2. DB๋Š” ๋‚˜์ค‘์— ๋ฐฐ์น˜๋กœ ์—…๋ฐ์ดํŠธ (๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…)
  await queue.add('updatePostViews', { postId })
}

๐Ÿ”ง ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์“ฐ๋Š” Redis ๋ช…๋ น์–ด๋“ค

๊ธฐ๋ณธ ๋ช…๋ น์–ด

# ๋ฌธ์ž์—ด ์ €์žฅ (TTL: Time To Live)
SET user:123 "{'name': 'john', 'age': 30}" EX 3600

# ๋ฐ์ดํ„ฐ ์กฐํšŒ
GET user:123

# ์‚ญ์ œ
DEL user:123

# ์กด์žฌ ํ™•์ธ
EXISTS user:123

ํ•ด์‹œ(Hash) ํ™œ์šฉ - ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ

# ํ•ด์‹œ๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ (๋” ํšจ์œจ์ !)
HSET user:123 name "john" age 30 email "john@email.com"

# ํŠน์ • ํ•„๋“œ๋งŒ ์กฐํšŒ
HGET user:123 name

# ์ „์ฒด ์ •๋ณด ์กฐํšŒ
HGETALL user:123

๋ฆฌ์ŠคํŠธ(List) ํ™œ์šฉ - ์ตœ์‹  ๊ฒŒ์‹œ๊ธ€

# ์ตœ์‹  ๊ฒŒ์‹œ๊ธ€ ID ์ถ”๊ฐ€ (์•ž์ชฝ์—)
LPUSH recent:posts 456 789 123

# ์ตœ์‹  5๊ฐœ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ
LRANGE recent:posts 0 4

๐Ÿ“ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ฟ€ํŒ 5๊ฐ€์ง€

1. ์ ์ ˆํ•œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„(TTL) ์„ค์ •

// ๋ฐ์ดํ„ฐ ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ TTL ์กฐ์ ˆ
await redis.setex('user:profile:123', 3600, data)      // 1์‹œ๊ฐ„
await redis.setex('product:list', 1800, data)          // 30๋ถ„  
await redis.setex('trending:posts', 600, data)         // 10๋ถ„

2. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋ชจ๋‹ˆํ„ฐ๋ง

# Redis ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ
redis-cli info memory

# ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ์„ค์ • (4GB)
redis-cli config set maxmemory 4gb
redis-cli config set maxmemory-policy allkeys-lru

3. Connection Pool ์‚ฌ์šฉ

// ์—ฐ๊ฒฐ ํ’€๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ
const redis = new Redis({
  host: 'localhost',
  port: 6379,
  maxRetriesPerRequest: 3,
  retryDelayOnFailover: 100,
  // ์—ฐ๊ฒฐ ํ’€ ์„ค์ •
  lazyConnect: true,
  maxLoadingTimeout: 5000
})

4. ๋ฐฐ์น˜ ์ž‘์—…์œผ๋กœ ์„ฑ๋Šฅ ๊ฐœ์„ 

// Pipeline์œผ๋กœ ์—ฌ๋Ÿฌ ๋ช…๋ น์–ด ํ•œ ๋ฒˆ์— ์‹คํ–‰
const pipeline = redis.pipeline()
pipeline.set('key1', 'value1')
pipeline.set('key2', 'value2')
pipeline.set('key3', 'value3')
await pipeline.exec() // ํ•œ ๋ฒˆ์— ์‹คํ–‰!

5. ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์„ ํƒ

  • ๋ฌธ์ž์—ด: ๊ฐ„๋‹จํ•œ ๊ฐ’ ์ €์žฅ
  • ํ•ด์‹œ: ๊ฐ์ฒด ์ •๋ณด (์‚ฌ์šฉ์ž, ์ƒํ’ˆ)
  • ๋ฆฌ์ŠคํŠธ: ์ˆœ์„œ๊ฐ€ ์žˆ๋Š” ๋ฐ์ดํ„ฐ (์ตœ์‹  ๊ธ€)
  • ์ง‘ํ•ฉ: ์ค‘๋ณต ์—†๋Š” ๋ฐ์ดํ„ฐ (ํƒœ๊ทธ, ์นดํ…Œ๊ณ ๋ฆฌ)
  • ์ •๋ ฌ๋œ ์ง‘ํ•ฉ: ๋žญํ‚น, ์ˆœ์œ„ (์ธ๊ธฐ ๊ธ€)

๐Ÿšจ Redis ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜์‚ฌํ•ญ

1. ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ๋Œ€๋น„

Redis๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋‹ˆ๊นŒ RAM์ด ๋ถ€์กฑํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ผ์š”.

// ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ์ž๋™ ์‚ญ์ œ ์ •์ฑ… ์„ค์ •
redis.config('SET', 'maxmemory-policy', 'allkeys-lru')

2. ์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์บ์‹œ๋„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ด์š”.

async function updateUser(userId, newData) {
  // 1. DB ์—…๋ฐ์ดํŠธ
  await db.user.update(userId, newData)
  
  // 2. ๊ด€๋ จ ์บ์‹œ ์‚ญ์ œ
  await redis.del(`user:${userId}`)
  await redis.del(`user:profile:${userId}`)
}

3. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€

์บ์‹œ์™€ DB ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์–ด์š”. ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” TTL์„ ์งง๊ฒŒ ์„ค์ •ํ•˜์„ธ์š”.

๐ŸŽฏ ์‹ค์ œ ์„ฑ๋Šฅ ๊ฐœ์„  ์‚ฌ๋ก€

Before (Redis ์‚ฌ์šฉ ์ „)

  • ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ: 500ms
  • ์ƒํ’ˆ ๋ชฉ๋ก ๋กœ๋”ฉ: 2์ดˆ
  • ๋™์‹œ ์ ‘์†์ž: 100๋ช…์—์„œ ์„œ๋ฒ„ ๋‹ค์šด

After (Redis ์‚ฌ์šฉ ํ›„)

  • ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ: 5ms (100๋ฐฐ ๋น ๋ฆ„!)
  • ์ƒํ’ˆ ๋ชฉ๋ก ๋กœ๋”ฉ: 50ms (40๋ฐฐ ๋น ๋ฆ„!)
  • ๋™์‹œ ์ ‘์†์ž: 1000๋ช…๋„ ์•ˆ์ •์ 

๊ฒฐ๊ณผ: TPS(์ดˆ๋‹น ์ฒ˜๋ฆฌ๋Ÿ‰) 500 โ†’ 5000์œผ๋กœ 10๋ฐฐ ํ–ฅ์ƒ!

๐Ÿ”ฎ ๋งˆ๋ฌด๋ฆฌ: Redis๋กœ ์„ฑ๋Šฅ ํ˜์‹ ํ•˜๊ธฐ

Redis ์บ์‹ฑ์€ ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ์˜ˆ์š”. ํŠนํžˆ:

โœ… ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„  - ๋น ๋ฅธ ์‘๋‹ต ์†๋„
โœ… ์„œ๋ฒ„ ๋น„์šฉ ์ ˆ์•ฝ - DB ๋ถ€ํ•˜ ๊ฐ์†Œ
โœ… ํ™•์žฅ์„ฑ ํ™•๋ณด - ๋” ๋งŽ์€ ์‚ฌ์šฉ์ž ์ˆ˜์šฉ ๊ฐ€๋Šฅ

์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ์บ์‹ฑ ๋ฒ”์œ„๋ฅผ ๋„“ํ˜€๋‚˜๊ฐ€์„ธ์š”. ์ฒ˜์Œ์—” ์‚ฌ์šฉ์ž ์„ธ์…˜๋งŒ ์บ์‹ฑํ•˜๋‹ค๊ฐ€, ๋‚˜์ค‘์—” ์ „์ฒด API ์‘๋‹ต๊นŒ์ง€ ์บ์‹ฑํ•  ์ˆ˜ ์žˆ์–ด์š”.

"๋น ๋ฅธ ์›น์‚ฌ์ดํŠธ = ์„ฑ๊ณตํ•˜๋Š” ์„œ๋น„์Šค"

์—ฌ๋Ÿฌ๋ถ„๋„ Redis๋กœ ์‚ฌ์šฉ์ž๋“ค์ด "์™€, ์ด ์‚ฌ์ดํŠธ ์ง„์งœ ๋น ๋ฅด๋‹ค!"๋ผ๊ณ  ๊ฐํƒ„ํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”! ๐Ÿš€


์ด ๊ธ€์ด ๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด ์ข‹์•„์š”์™€ ๋Œ“๊ธ€๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ Redis ๊ฒฝํ—˜์„ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”!

๋Œ“๊ธ€ 0๊ฐœ

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค

์ฒซ ๋ฒˆ์งธ ๋Œ“๊ธ€์„ ์ž‘์„ฑํ•ด๋ณด์„ธ์š”!