Docker/Linux/macOS ์ค์น ๋ฐฉ๋ฒ, String, Hash, List, Set, Sorted Set 5๋ ์๋ฃ๊ตฌ์กฐ, TTL ํค ๊ด๋ฆฌ, Pub/Sub, ํธ๋์ญ์
, RDB/AOF ์์์ฑ, ์บ์ ์ ๋ต ๋ฐ ์ค๋ฌด ์ด์ ์ต์ ํ ๋ ํผ๋ฐ์ค
## 1. Redis ์ค์น & ์คํ ๊ฐ์ด๋ (Installation: Docker, Linux, macOS)
```bash
# ๐ณ [1] Docker ์ปจํ
์ด๋ ์คํ (๊ฐ์ฅ ๊ฐํธํ๊ณ ์ค๋ฌด ๊ถ์ฅ)
# 1-1. ์ด๊ฐ๋จ ๊ธฐ๋ณธ ์คํ (ํฌํธ 6379 ํฌ์๋ฉ)
docker run -d --name redis -p 6379:6379 redis:latest
# 1-2. ์ค๋ฌด ๊ถ์ฅ ์คํ (๋น๋ฐ๋ฒํธ ์ค์ + ๋ฐ์ดํฐ ๋ณผ๋ฅจ ์์ํ + AOF ํ์ฑํ)
docker run -d \
--name redis \
-p 6379:6379 \
-v redis_data:/data \
--restart always \
redis:latest redis-server --requirepass "mypassword123" --appendonly yes
# ์ปจํ
์ด๋ ๋ด redis-cli ์ ์
docker exec -it redis redis-cli -a "mypassword123"
```
```yaml
# 1-3. docker-compose.yml ํ
ํ๋ฆฟ
version: '3.8'
services:
redis:
image: redis:7-alpine
container_name: redis-server
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --requirepass "mypassword123" --appendonly yes
volumes:
redis_data:
```
```bash
# ๐ง [2] Linux (Ubuntu / Debian) ๊ณต์ APT ํจํค์ง ์ค์น
sudo apt update && sudo apt install -y lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt update
sudo apt install -y redis
# ์๋น์ค ์คํ ๋ฐ ๋ถํ
์ ์๋ ์์ ๋ฑ๋ก
sudo systemctl enable --now redis-server
sudo systemctl status redis-server
# ๐ [3] macOS (Homebrew ์ค์น)
brew install redis
# ๋ฐฑ๊ทธ๋ผ์ด๋ ์๋น์ค ์์ ๋ฐ ์ค์ง
brew services start redis
brew services stop redis
# ๐ช [4] Windows ํ๊ฒฝ
# Redis ๊ณต์์ Linux ๋ค์ดํฐ๋ธ๋ฅผ ์ง์ํ๋ฏ๋ก WSL2(Ubuntu) ์ค์น ํ apt ์ค์น ๋๋ Docker Desktop ์คํ ๊ถ์ฅ
```
## 2. CLI ์ ์ & ์๋ฒ ๊ธฐ๋ณธ ๋ช
๋ น์ด (CLI & Server Management)
```bash
# ๋ก์ปฌ redis-cli ์ ์
redis-cli
# ์๊ฒฉ ํธ์คํธ ๋ฐ ํน์ ํฌํธ, ๋น๋ฐ๋ฒํธ ์ธ์ฆ ์ ์
redis-cli -h 127.0.0.1 -p 6379 -a "mypassword"
# CLI์์ ํ๊ตญ์ด ๋ฑ UTF-8 ๋ฌธ์ ๊นจ์ง ๋ฐฉ์ง ์ ์
redis-cli --raw
```
```text
# [๊ธฐ๋ณธ ์๋ฒ ์ํ ํ์ธ ๋ฐ ์ง๋จ]
PING # ์ฐ๊ฒฐ ํ์ธ (์๋ต: PONG)
INFO # ์๋ฒ ์ ์ฒด ์ ๋ณด(๋ฉ๋ชจ๋ฆฌ, ํด๋ผ์ด์ธํธ, CPU, ํต๊ณ ๋ฑ) ์กฐํ
INFO memory # ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์์ธ(used_memory_human ๋ฑ) ํ์ธ
SELECT 0 # 0๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ํ (๊ธฐ๋ณธ 0~15๋ฒ ์ง์)
DBSIZE # ํ์ฌ ์ ํ๋ DB์ ์ ์ฒด ํค ๊ฐ์ ๋ฐํ
FLUSHDB # ํ์ฌ ์ ํ๋ DB์ ๋ชจ๋ ํค ์ฆ์ ์ญ์ (์ฃผ์!)
FLUSHALL # Redis ์ธ์คํด์ค์ ๋ชจ๋ DB ๋ฐ์ดํฐ ์ ์ฒด ์ญ์ (์ด์ ํ๊ฒฝ ์ ๋ ๊ธ์ง!)
MONITOR # ์๋ฒ์์ ์ค์๊ฐ ์ฒ๋ฆฌ๋๋ ๋ชจ๋ ๋ช
๋ น์ด ์คํธ๋ฆฌ๋ฐ (๋๋ฒ๊น
์ฉ, ์ฑ๋ฅ ์ ํ ์ ๋ฐ)
CLIENT LIST # ํ์ฌ ์ฐ๊ฒฐ๋ ๋ชจ๋ ํด๋ผ์ด์ธํธ ์ธ์
๋ชฉ๋ก ์กฐํ
```
## 3. String ์๋ฃ๊ตฌ์กฐ (๋ฌธ์์ด, ์ซ์ ์นด์ดํฐ, ๋ถ์ฐ๋ฝ)
```text
# [๋จ์ผ ๊ฐ ์ ์ฅ ๋ฐ ์กฐํ]
SET user:1:name "lucky" # ํค์ ๋ฌธ์์ด ์ ์ฅ
GET user:1:name # ํค ๊ฐ ์กฐํ -> "lucky"
MSET k1 "v1" k2 "v2" # ์ฌ๋ฌ ํค-๊ฐ์ ์์์ ์ผ๋ก ํ ๋ฒ์ ์ ์ฅ
MGET k1 k2 # ์ฌ๋ฌ ํค๋ฅผ ํ ๋ฒ์ ์กฐํ
# [๋ง๋ฃ ์๊ฐ(TTL) ํฌํจ ์ ์ฅ (์ด ๋จ์)]
SETEX session:token 3600 "abc123xyz" # 3600์ด(1์๊ฐ) ํ ์๋ ๋ง๋ฃ
# [SETNX: ํค๊ฐ ์์ ๋๋ง ์ ์ฅ (๋ถ์ฐ ๋ฝ์ ๊ธฐ๋ณธ ์๋ฆฌ)]
SETNX lock:order:100 "owner1" # ํค๊ฐ ์์ผ๋ฉด 1(์ฑ๊ณต), ์ด๋ฏธ ์กด์ฌํ๋ฉด 0(์คํจ)
# ์ค๋ฌด ๊ถ์ฅ ๋ถ์ฐ ๋ฝ ํจํด: SET key val NX EX seconds (์์์ ๋ง๋ฃ ๋ณด์ฅ)
SET lock:order:100 "worker-1" NX EX 10
# [์ซ์ ์ฆ๊ฐ (์์์ ์นด์ดํฐ, ์กฐํ์, ์ฌ๊ณ ๊ด๋ฆฌ)]
INCR page:view:count # ๊ฐ์ 1 ์ฆ๊ฐ (์์ผ๋ฉด 0 ์์ฑ ํ 1๋ก ์ค์ )
DECR product:stock:55 # ๊ฐ์ 1 ๊ฐ์
INCRBY user:point 500 # ์ง์ ํ ์ ์๋งํผ ์ฆ๊ฐ
INCRBYFLOAT temp 0.5 # ์์์ ์ค์ ๋จ์ ์ฆ๊ฐ
STRLEN user:1:name # ๋ฌธ์์ด ๊ธธ์ด ๋ฐํ
```
## 4. Hash ์๋ฃ๊ตฌ์กฐ (ํ๋-๊ฐ ์์ ๊ฐ์ฒด/์ํฐํฐ ์ ์ฅ)
```text
# ๋จ์ผ ํ๋ ์ค์ ๋ฐ ์กฐํ
HSET user:100 name "lucky" email "lucky@test.com" age 30 # ์ฌ๋ฌ ํ๋ ๋์ ์ค์
HGET user:100 name # "lucky"
HGETALL user:100 # ๋ชจ๋ ํ๋์ ๊ฐ ์ ์กฐํ
# ํ๋ ์กด์ฌ ์ฌ๋ถ ๋ฐ ์ญ์
HEXISTS user:100 email # ํ๋ ์กด์ฌ ์ฌ๋ถ ํ์ธ (1 or 0)
HDEL user:100 age # ํน์ ํ๋ ์ญ์
HLEN user:100 # ํด์์ ํฌํจ๋ ํ๋ ๊ฐ์ ๋ฐํ
HKEYS user:100 # ๋ชจ๋ ํ๋๋ช
๋ชฉ๋ก๋ง ๋ฐํ
HVALS user:100 # ๋ชจ๋ ํ๋์ ๊ฐ ๋ชฉ๋ก๋ง ๋ฐํ
# ํ๋ ์ซ์ ์ฆ๊ฐ
HINCRBY user:100 loginCount 1 # ํด์ ๋ด ์ซ์ ํ๋ ์์์ ์ฆ๊ฐ
# [Tip] Hash๋ RDBMS์ ๋จ์ผ ํ
์ด๋ธ ๋ ์ฝ๋(Row)๋ฅผ ํํํ๊ธฐ์ ๊ฐ์ฅ ์ ํฉ
```
## 5. List ์๋ฃ๊ตฌ์กฐ (์๋ฐฉํฅ ํ, ์ต๊ทผ ๋ชฉ๋ก, ๋ฉ์์ง ๋ฒํผ)
```text
# [์ฝ์
: ์ผ์ชฝ(Head) / ์ค๋ฅธ์ชฝ(Tail)]
LPUSH queue:job "task-1" "task-2" # ์ผ์ชฝ(์)์์ ๋ฐ์ดํฐ ์ฝ์
RPUSH queue:job "task-3" # ์ค๋ฅธ์ชฝ(๋ค)์์ ๋ฐ์ดํฐ ์ฝ์
# [์กฐํ & ์ฌ๋ผ์ด์ฑ]
LRANGE queue:job 0 -1 # ์ ์ฒด ์์ ์กฐํ (0์ ์ฒซ๋ฒ์งธ, -1์ ๋ง์ง๋ง)
LRANGE queue:job 0 9 # ์ต์ 10๊ฐ ์์ ์กฐํ (ํ์ด์ง)
LINDEX queue:job 0 # ํน์ ์ธ๋ฑ์ค์ ์์ ์กฐํ
LLEN queue:job # ๋ฆฌ์คํธ ์ ์ฒด ๊ธธ์ด ๋ฐํ
# [์ถ์ถ(Pop) & ํ ๊ตฌํ]
LPOP queue:job # ์ผ์ชฝ ์ฒซ ์์ ๊บผ๋ด๊ธฐ (FIFO ํ ๊ตฌํ ์ RPUSH + LPOP)
RPOP queue:job # ์ค๋ฅธ์ชฝ ๋ง์ง๋ง ์์ ๊บผ๋ด๊ธฐ
# [๋ธ๋กํน Pop: ์์
ํ ๋๊ธฐ์ฉ (Consumer ๊ตฌํ)]
# ํ์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ฌ ๋๊น์ง ์ต๋ 10์ด๊ฐ ๋๊ธฐ (0์ด๋ฉด ๋ฌดํ ๋๊ธฐ)
BLPOP queue:job 10
BRPOP queue:job 10
# [์ต๊ทผ N๊ฐ ํญ๋ชฉ๋ง ์ ์ง (๋ก๊ทธ ๋กค๋ง, ์ต๊ทผ ๋ณธ ์ํ)]
LPUSH user:recent:items "item-99"
LTRIM user:recent:items 0 19 # 0~19๋ฒ์งธ(์ด 20๊ฐ)๋ง ๋จ๊ธฐ๊ณ ๋๋จธ์ง ์๋ ์ ๊ฑฐ
```
## 6. Set ์๋ฃ๊ตฌ์กฐ (๊ณ ์ ๊ฐ ์งํฉ, ์ข์์/ํ๋ก์ฐ, ๊ต์งํฉ ์ฐ์ฐ)
```text
# [์์ ์ถ๊ฐ & ์กฐํ]
SADD post:10:likes "user-1" "user-2" "user-3" # ์งํฉ์ ์ถ๊ฐ (์ค๋ณต ์๋ ๋ฌด์)
SMEMBERS post:10:likes # ์ ์ฒด ์์ ๋ชฉ๋ก ๋ฐํ
SISMEMBER post:10:likes "user-2" # ํน์ ์์ ํฌํจ ์ฌ๋ถ ํ์ธ (1 or 0)
SCARD post:10:likes # ์งํฉ ํฌ๊ธฐ(์ข์์ ์ด ๊ฐ์) ๋ฐํ
SREM post:10:likes "user-3" # ํน์ ์์ ์ ๊ฑฐ
SPOP post:10:likes # ๋ฌด์์ 1๊ฐ ์์๋ฅผ ๊บผ๋ด๋ฉฐ ์ ๊ฑฐ (์ถ์ฒจ ์ด๋ฒคํธ)
SRANDMEMBER post:10:likes 2 # ์ ๊ฑฐํ์ง ์๊ณ ๋ฌด์์ 2๊ฐ ์ถ์ถ
# [์งํฉ ์ฐ์ฐ (๊ณตํต ๊ด์ฌ์ฌ, ์น๊ตฌ ์ถ์ฒ)]
SADD tag:java "post-1" "post-2" "post-3"
SADD tag:spring "post-2" "post-3" "post-4"
SINTER tag:java tag:spring # ๊ต์งํฉ (AND): ๋ ํ๊ทธ ๋ชจ๋ ํฌํจ๋ ๊ฒ์๊ธ ("post-2", "post-3")
SUNION tag:java tag:spring # ํฉ์งํฉ (OR): ์ด๋ ํ๋๋ผ๋ ํฌํจ๋ ๊ฒ์๊ธ
SDIFF tag:java tag:spring # ์ฐจ์งํฉ (NOT): java ํ๊ทธ์๋ง ์๊ณ spring์๋ ์๋ ๊ธ
```
## 7. Sorted Set (ZSet: ์ ์ ๊ธฐ๋ฐ ์ ๋ ฌ, ๋ญํน, ์ค์ฝ์ด๋ณด๋)
```text
# [์์ ์ถ๊ฐ (ZADD key score member)]
ZADD leaderboard 1500 "user:A"
ZADD leaderboard 2300 "user:B" 1800 "user:C"
# [์์ & ๋ฒ์ ์กฐํ]
# ์ ์ ์ค๋ฆ์ฐจ์(๋ฎ์ ์ ์ ์) ์กฐํ
ZRANGE leaderboard 0 -1 WITHSCORES
# ์ ์ ๋ด๋ฆผ์ฐจ์(๋์ ์ ์ ์, ๋ญํน 1~3์ ์กฐํ)
ZREVRANGE leaderboard 0 2 WITHSCORES
# [ํน์ ์ฌ์ฉ์์ ์์ ์กฐํ (0๋ถํฐ ์์)]
ZREVRANK leaderboard "user:B" # 1์๋ฉด 0 ๋ฐํ
ZRANK leaderboard "user:A"
# [์ ์ ๋ฒ์ ๊ธฐ๋ฐ ์กฐํ & ์ ์ ์ฆ๊ฐ]
ZRANGEBYSCORE leaderboard 1500 2000 # 1500์ ์ด์ 2000์ ์ดํ ์ฌ์ฉ์
ZSCORE leaderboard "user:A" # user:A์ ํ์ฌ ์ ์ ๋ฐํ
ZINCRBY leaderboard 50 "user:A" # user:A์ ์ ์๋ฅผ 50์ ๊ฐ์ฐ
# [์์ ์ ๊ฑฐ]
ZREM leaderboard "user:C"
ZREMRANGEBYRANK leaderboard 0 10 # ํ์ 10๋ช
์ผ๊ด ์ญ์
```
## 8. ํค(Key) ๊ด๋ฆฌ & TTL (Time To Live)
```text
# [๋ง๋ฃ ์๊ฐ ์ค์ & ํ์ธ]
EXPIRE session:user:1 1800 # 1800์ด(30๋ถ) ํ ์๋ ์ญ์
PEXPIRE key 5000 # 5000๋ฐ๋ฆฌ์ด(5์ด) ํ ๋ง๋ฃ
TTL session:user:1 # ๋จ์ ์๊ฐ(์ด) ํ์ธ (-1: ๋ฌด์ ํ, -2: ํค ์์)
PTTL key # ๋จ์ ์๊ฐ(๋ฐ๋ฆฌ์ด) ํ์ธ
PERSIST session:user:1 # ์ค์ ๋ ๋ง๋ฃ ์๊ฐ์ ์ ๊ฑฐํ๊ณ ์๊ตฌ ๋ณด์กด
# [ํค ์ญ์ & ํ์
ํ์ธ]
DEL key1 key2 # ๋๊ธฐ์ ์ฆ์ ์ญ์ (ํฐ ์ปฌ๋ ์
์ญ์ ์ ์๋ฒ ๋ฉ์ถค ์ฃผ์)
UNLINK key1 key2 # ๋น๋๊ธฐ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ญ์ (์ค๋ฌด ํ์ ๊ถ์ฅ!)
EXISTS key1 # ํค ์กด์ฌ ์ฌ๋ถ (1 or 0)
TYPE key1 # ํค์ ์๋ฃ๊ตฌ์กฐ ํ์
๋ฐํ (string, hash, list...)
# [์ด์ ํ๊ฒฝ ์ฃผ์: ํค ๊ฒ์]
KEYS * # โ ๏ธ ์ ์ ํ์ (O(N)), ์ด์ DB์์ ์คํ ์ ์๋น์ค ๋ง๋น!
# ๊ถ์ฅ ๋์: ์ปค์ ๊ธฐ๋ฐ ์์ ํ ๋ถํ ์กฐํ (SCAN)
SCAN 0 MATCH user:* COUNT 100 # 0๋ฒ ์ปค์๋ถํฐ 100๊ฐ์ฉ ์ํ
```
## 9. Pub/Sub (๋ฐํ/๊ตฌ๋
๋ฉ์์ง ํจํด)
```text
# [๊ตฌ๋
์ (Subscriber) - ํฐ๋ฏธ๋ A]
SUBSCRIBE news:sports # ํน์ ์ฑ๋ ๊ตฌ๋
(๋ฉ์์ง๊ฐ ๋ค์ด์ฌ ๋๊น์ง ๋๊ธฐ)
PSUBSCRIBE news:* # ํจํด ๊ธฐ๋ฐ ์ฑ๋ ๊ตฌ๋
(news:๋ก ์์ํ๋ ๋ชจ๋ ์ฑ๋)
# [๋ฐํ์ (Publisher) - ํฐ๋ฏธ๋ B]
PUBLISH news:sports "Son scores a hat-trick!" # ๊ตฌ๋
์๋ค์๊ฒ ๋ฉ์์ง ์ค์๊ฐ ๋ธ๋ก๋์บ์คํธ
# [๊ตฌ๋
ํด์ ]
UNSUBSCRIBE news:sports
PUNSUBSCRIBE news:*
# ๐ก ์ฃผ์: Redis Pub/Sub์ ๋ฉ์์ง ์์์ฑ(์ ์ฅ)์ ์ง์ํ์ง ์์.
# ์ปจ์๋จธ๊ฐ ๋ค์ด๋๋ฉด ๋ฉ์์ง ์ ์ค๋จ. ์์ ๋ฉ์์ง์ด ํ์ํ๋ฉด Redis Streams ์ฌ์ฉ ๊ถ์ฅ.
```
## 10. ํธ๋์ญ์
& ํ์ดํ๋ผ์ด๋ (Transactions & Pipelining)
```text
# [1] ํธ๋์ญ์
(MULTI / EXEC ์์์ ๋ธ๋ก)
MULTI # ํธ๋์ญ์
๋ธ๋ก ์์ (์ดํ ๋ช
๋ น์ ํ์ ์์)
SET account:A 900
INCRBY account:B 100
EXEC # ํ์ ๋๊ธฐ ์ค์ธ ๋ชจ๋ ๋ช
๋ น์ ์ผ๊ด ์คํ
# (์ทจ์ ์์๋ DISCARD)
# [2] ๋๊ด์ ๋ฝ (Optimistic Lock: WATCH)
WATCH product:stock:1 # ํค์ ๋ณ๊ฒฝ์ ๊ฐ์
val = GET product:stock:1
MULTI
SET product:stock:1 (val - 1)
EXEC # ๊ฐ์ ์ค ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๊ฐ ํค๋ฅผ ์์ ํ์ผ๋ฉด EXEC ์คํจ(nil)
# [3] ํ์ดํ๋ผ์ด๋ (Pipelining)
# ์ฌ๋ฌ ๋ช
๋ น์ ๋คํธ์ํฌ RTT(์๋ณต ์๊ฐ) ์์ด ํ ๋ฒ์ ์ ์กํ์ฌ ์ฒ๋ฆฌ ์๋ 10๋ฐฐ ์ด์ ํฅ์
# redis-cli --pipe < commands.txt
```
## 11. ์์์ฑ & ๋ฐฑ์
(Persistence: RDB vs AOF)
```text
# [1] RDB (Redis Database Snapshot)
# ํน์ ์์ ์ ๋ฉ๋ชจ๋ฆฌ ์ ์ฒด๋ฅผ ๋คํ ํ์ผ(dump.rdb)๋ก ์์ถ ์ ์ฅ
BGSAVE # ๋ฐฑ๊ทธ๋ผ์ด๋ ๋น๋๊ธฐ ์ค๋
์ท ์ ์ฅ ์คํ
LASTSAVE # ์ต๊ทผ ์ค๋
์ท ์ฑ๊ณต ํ์์คํฌํ ๋ฐํ
# [2] AOF (Append Only File)
# ๋ชจ๋ ์ฐ๊ธฐ/์์ ๋ช
๋ น์ด๋ฅผ ๋ก๊ทธ ํ์ผ(appendonly.aof)์ ์์ฐจ ๊ธฐ๋ก
BGREWRITEAOF # AOF ํ์ผ์ ์์ถ ์ฌ์์ฑ(์ต์ ํ) ์คํ
```
```text
# [redis.conf ํต์ฌ ์์์ฑ ๊ถ์ฅ ์ค์ ]
# RDB ์ฃผ๊ธฐ ์ค์ : save <์ด> <์ต์๋ณ๊ฒฝ๊ฑด์>
save 900 1 # 900์ด(15๋ถ) ๋์ 1๊ฑด ์ด์ ๋ณ๊ฒฝ ์
save 300 10 # 300์ด(5๋ถ) ๋์ 10๊ฑด ์ด์ ๋ณ๊ฒฝ ์
save 60 10000 # 60์ด ๋์ 10000๊ฑด ์ด์ ๋ณ๊ฒฝ ์
# AOF ํ์ฑํ (RDB๋ณด๋ค ๋ฐ์ดํฐ ์ ์ค ์ต์ํ)
appendonly yes
appendfsync everysec # ๋งค 1์ด๋ง๋ค ๋์คํฌ ๋๊ธฐํ (์ฑ๋ฅ๊ณผ ์์ ์ฑ ์ต์ ๊ท ํ)
```
## 12. ์ค๋ฌด ์บ์ฑ ์ ๋ต & Spring Boot ์ฐ๋
```text
# [๋ํ์ ์ธ ์บ์ ํจํด]
1. Cache-Aside (Look-Aside):
- ์ฝ๊ธฐ ์์ฒญ ์ ์บ์ ๋จผ์ ํ์ธ -> Cache Miss ๋ฐ์ ์ DB ์กฐํ ํ ์บ์์ ์ ์ฅ
- ์ค๋ฌด์์ ๊ฐ์ฅ ๋ณดํธ์ ์ผ๋ก ์ฌ์ฉ
2. Write-Around:
- ๋ชจ๋ ์ฐ๊ธฐ๋ DB์๋ง ์ง์ ๊ธฐ๋ก -> ์ฝ๊ธฐ ์์ ์๋ง ์บ์ ๊ฐฑ์
3. Write-Through:
- ์บ์์ DB์ ๋์์ ์ฐ๊ธฐ ์ํ (์ผ๊ด์ฑ ๋์, ์ฐ๊ธฐ ์ง์ฐ ์ฆ๊ฐ)
```
```java
// Spring Boot 3.x Spring Data Redis ์ค์
// build.gradle: implementation 'org.springframework.boot:spring-boot-starter-data-redis'
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key๋ ๋ฌธ์์ด ์ง๋ ฌํ, Value๋ JSON ์ง๋ ฌํ ์ค์ (๊ฐ๋
์ฑ & ํธํ์ฑ)
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
// ์๋น์ค ๊ณ์ธต ์ด๋
ธํ
์ด์
๊ธฐ๋ฐ ์บ์ฑ
@Service
public class ProductService {
// ์กฐํ ๊ฒฐ๊ณผ ์บ์ฑ (ํค: product::123, 10๋ถ TTL)
@Cacheable(value = "product", key = "#id")
public ProductDto getProduct(Long id) {
return productRepository.findById(id).map(ProductDto::from).orElseThrow();
}
// ์์ ์ ๊ธฐ์กด ์บ์ ์ญ์ (๋ฐ์ดํฐ ๋ถ์ผ์น ๋ฐฉ์ง)
@CacheEvict(value = "product", key = "#id")
public void updateProduct(Long id, ProductUpdateDto dto) {
// DB ์
๋ฐ์ดํธ ๋ก์ง
}
}
```
## 13. ์ด์ ํธ๋ฌ๋ธ์ํ
& ์ฑ๋ฅ ์ฃผ์์ฌํญ (Production Tips)
```text
# [1] OOM(Out of Memory) ๋ฐฉ์ง๋ฅผ ์ํ ๋ฉ๋ชจ๋ฆฌ ์ ์ฑ
(maxmemory-policy)
# redis.conf ์ค์ :
maxmemory 4gb
maxmemory-policy allkeys-lru # ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ ๊ฐ์ฅ ์ค๋ ์ฌ์ฉ๋์ง ์์ ํค๋ถํฐ ํด์ถ
# ์ต์
: volatile-lru(TTL์๋ ํค ์ค LRU), noeviction(์ถ๊ฐ ์ฐ๊ธฐ ๊ฑฐ๋ถ, ์๋ฌ ๋ฐ์)
# [2] ๋๋ฆฐ ์ฟผ๋ฆฌ ๋ชจ๋ํฐ๋ง (Slowlog)
SLOWLOG GET 10 # ์ต๊ทผ ๋๋ฆฐ ์ฟผ๋ฆฌ 10๊ฐ ์กฐํ
CONFIG SET slowlog-log-slower-than 10000 # 10000๋ง์ดํฌ๋ก์ด(10ms) ์ด์ ๊ฑธ๋ฆฐ ์ฟผ๋ฆฌ ๋ก๊น
# [3] ๋น
ํค(Big Key) ์ง๋จ
# ํ๋์ ํค์ ์๋ง~์์ญ๋ง ๊ฐ์ ์์๊ฐ ๋ค์ด์์ผ๋ฉด ๋คํธ์ํฌ ๋ณ๋ชฉ ๋ฐ ์ง์ฐ ์ ๋ฐ
redis-cli --bigkeys # ๊ฐ์ฅ ์ฉ๋์ ๋ง์ด ์ฐจ์งํ๋ ๋ํ ํค ์ค์บ ๋ถ์
# [4] ์ค๋ฌด 5๋ ๊ธ๊ธฐ ์ฌํญ
1. ์ด์ DB์์ KEYS * ์คํ ๊ธ์ง -> SCAN ๋ช
๋ น์ด๋ก ๋์ฒด
2. ๋์ฉ๋ ์ปฌ๋ ์
(์๋ง ๊ฑด) ๋์ DEL ๊ธ์ง -> UNLINK๋ก ๋น๋๊ธฐ ์ญ์
3. ๋ชจ๋ ํค์ ์ ์ ํ TTL(๋ง๋ฃ์๊ฐ) ๋ฐ๋์ ๋ถ์ฌ
4. ๋จ์ผ Redis ์ธ์คํด์ค์ ์ง๋์น๊ฒ ํฐ ๋ฉ๋ชจ๋ฆฌ(16GB ์ด์) ํ ๋น ์ง์ (Fork ์ ์ค์ ์ํ)
5. ๊ธฐ๋ณธ ํฌํธ(6379) ์ธ๋ถ ๊ณต์ธ IP ๋ฌด๋จ ๋
ธ์ถ ๊ธ์ง (๋ฐ์ธ๋ฉ ์ ํ & requirepass ์ค์ ํ์)
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์