Post

레디스 기본 개념

레디스 기본 개념

3장 : 레디스 기본 개념

들어가며

레디스는 단순한 키-값 저장소가 아니다. 다양한 자료 구조를 지원하는 데이터 구조 서버 라고 표현하는 것이 더 정확하다. 기존의 키-값 저장소가 문자열 키와 문자열 값만을 연결했다면, 레디스는 String, List, Hash, Set, Sorted Set 등 복잡한 자료 구조를 지원한다.

이번 장에서는 레디스가 제공하는 자료 구조들의 특징과 사용법을 살펴보고, 키를 효율적으로 관리하는 방법까지 알아본다.


레디스의 자료 구조

String

String은 레디스에서 가장 기본적인 자료 구조 다. 키와 연결할 수 있는 가장 단순한 유형의 값이며, 레디스의 키가 문자열이므로 문자열을 다른 문자열에 매핑하는 구조라고 볼 수 있다.

String 타입에는 모든 종류의 문자열(이진 데이터 포함)을 저장할 수 있다. 따라서 JPEG 이미지를 저장하거나 HTML fragment를 캐시하는 용도로 자주 사용한다. 저장할 수 있는 최대 크기는 512MB 다.

1
2
3
4
5
# 기본 SET/GET
127.0.0.1:6379> SET hello world
OK
127.0.0.1:6379> GET hello
"world"

기본적으로 기존 값은 새로 입력된 값으로 계속 대체된다. SET과 함께 NX 옵션 을 사용하면 지정한 키가 없을 때에만 새로운 키를 저장한다.

1
2
3
# 이미 hello라는 키가 있으므로 저장되지 않음
127.0.0.1:6379> SET hello newval NX
(nil)

SET과 함께 XX 옵션 을 사용하면 키가 이미 있을 때에만 새로운 값으로 덮어 씌우며, 새로운 키를 생성하지 않는다.

1
2
3
4
127.0.0.1:6379> SET hello newval XX
OK
127.0.0.1:6379> GET hello
"newval"

NX 옵션의 활용: 분산 락

NX 옵션은 분산 락(Distributed Lock) 구현에 핵심적으로 사용된다. 분산 락이란 여러 서버(프로세스)가 동시에 같은 자원에 접근할 때 한 번에 하나만 접근하도록 제어하는 장치다.

1
2
3
4
5
6
7
8
9
10
# 서버 A가 락 획득 시도 (10초 후 자동 만료)
127.0.0.1:6379> SET lock:order123 "serverA" NX EX 10
OK  # 성공!

# 서버 B가 락 획득 시도
127.0.0.1:6379> SET lock:order123 "serverB" NX EX 10
(nil)  # 실패 - 이미 A가 잡고 있음

# 서버 A가 작업 완료 후 락 해제
127.0.0.1:6379> DEL lock:order123

NX는 “없을 때만 생성”이므로 먼저 요청한 서버만 성공하고 나머지는 실패한다. EX 옵션으로 만료 시간을 설정하면 서버가 죽어서 락을 못 풀어도 자동으로 해제된다.

숫자 증감 연산

숫자 형태의 데이터를 저장하면 INCR, INCRBY, DECR, DECRBY 커맨드를 이용해 원자적으로 조작할 수 있다.

1
2
3
4
5
6
7
8
127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> INCR counter
(integer) 101
127.0.0.1:6379> INCRBY counter 50
(integer) 151
127.0.0.1:6379> DECR counter
(integer) 150

여기서 원자적(Atomic) 이란 여러 클라이언트가 동시에 INCR을 호출해도 값이 꼬이지 않는다는 의미다. 레디스는 싱글 스레드로 동작하기 때문에 명령어가 하나씩 순서대로 실행되어 동시성 문제가 발생하지 않는다.


List

List는 순서가 있는 문자열의 집합 이다. 내부적으로 Linked List 형태로 구현되어 있어 양 끝에서의 삽입과 삭제가 O(1)로 매우 빠르다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 왼쪽에 추가 (head)
127.0.0.1:6379> LPUSH mylist "a"
(integer) 1
127.0.0.1:6379> LPUSH mylist "b" "c"
(integer) 3
# 결과: [c, b, a]

# 오른쪽에 추가 (tail)
127.0.0.1:6379> RPUSH mylist "d"
(integer) 4
# 결과: [c, b, a, d]

# 왼쪽에서 꺼내기
127.0.0.1:6379> LPOP mylist
"c"

# 오른쪽에서 꺼내기
127.0.0.1:6379> RPOP mylist
"d"

# 범위 조회
127.0.0.1:6379> LRANGE mylist 0 -1
1) "b"
2) "a"

List는 큐(Queue)스택(Stack) 으로 활용할 수 있다.

  • 큐: LPUSH + RPOP (또는 RPUSH + LPOP)
  • 스택: LPUSH + LPOP

List의 내부 구조: Quicklist

레디스 3.2 버전부터 List는 Quicklist 라는 자료 구조로 구현된다. Quicklist는 Ziplist들을 Linked List로 연결한 형태다.

1
2
3
4
[quicklistNode] ↔ [quicklistNode] ↔ [quicklistNode]
       ↓                 ↓                 ↓
   [ziplist]         [ziplist]         [ziplist]
   [a][b][c]         [d][e][f]         [g][h][i]

Ziplist 는 연속된 메모리에 데이터를 압축 저장하여 메모리 효율이 높다. 순수 Linked List는 노드당 약 40바이트의 포인터 오버헤드가 발생하지만, Ziplist는 1~10바이트 수준이다.

하지만 Ziplist만 사용하면 앞에 데이터를 삽입할 때 전체를 복사해야 하는 문제가 있다. Quicklist는 작은 Ziplist들을 연결하여 메모리 효율과 성능을 모두 확보 한다.

시간 복잡도 정리

명령어 시간 복잡도 이유
LPUSH / RPUSH O(1) 양 끝은 바로 접근
LPOP / RPOP O(1) 양 끝은 바로 접근
LINDEX O(N) 인덱스까지 순회
LINSERT O(N) pivot 찾을 때까지 순회
LRANGE O(S+N) 시작점까지 이동 + 개수만큼

핵심: List는 양 끝 작업에 최적화되어 있다. 중간 작업이 많으면 다른 자료 구조를 고려해야 한다.


Hash

Hash는 필드-값 쌍을 저장 하는 자료 구조다. 하나의 키 아래에 여러 필드를 저장할 수 있어 객체를 표현하기에 적합하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 저장
127.0.0.1:6379> HSET user:123 name "kim" age 25 city "seoul"
(integer) 3

# 단일 필드 조회
127.0.0.1:6379> HGET user:123 name
"kim"

# 전체 조회
127.0.0.1:6379> HGETALL user:123
1) "name"
2) "kim"
3) "age"
4) "25"
5) "city"
6) "seoul"

# 여러 필드 조회
127.0.0.1:6379> HMGET user:123 name age
1) "kim"
2) "25"

# 필드 삭제
127.0.0.1:6379> HDEL user:123 city
(integer) 1

# 숫자 증가
127.0.0.1:6379> HINCRBY user:123 age 1
(integer) 26

String 여러 개 vs Hash 하나

사용자 정보를 저장할 때 두 가지 방법을 비교해보자.

1
2
3
4
5
6
7
8
9
# 방법 1: String 여러 개
SET user:123:name "kim"
SET user:123:age 25
SET user:123:city "seoul"
# 키 3개, 메모리 더 사용, 조회 3번

# 방법 2: Hash 하나
HSET user:123 name "kim" age 25 city "seoul"
# 키 1개, 메모리 적게 사용, 조회 1번

관련 데이터는 Hash로 묶는 것이 효율적 이다.

Hash의 내부 구조

Hash는 데이터 크기에 따라 두 가지 인코딩을 사용한다.

1
2
데이터가 작을 때: listpack (또는 ziplist)
데이터가 클 때:   hashtable

기준은 redis.conf에서 설정할 수 있다.

1
2
hash-max-listpack-entries 512   # 필드 512개 이하
hash-max-listpack-value 64      # 각 값 64바이트 이하

둘 다 만족하면 listpack, 하나라도 넘으면 hashtable로 변환된다. 작은 데이터에 listpack을 사용하는 이유는 해시테이블의 포인터 오버헤드 없이 메모리를 절약할 수 있기 때문이다.

HGETALL 주의사항

1
HGETALL user:123

필드가 100만 개면 100만 개를 전부 읽어서 반환한다. 그동안 레디스는 블로킹되어 다른 요청을 처리하지 못한다. 대량의 필드가 있다면 HSCAN 을 사용해야 한다.

1
2
HSCAN user:123 0 COUNT 100
# 100개씩 나눠서 조회 (커서 기반)


Set

Set은 중복 없는 문자열의 집합 이다. 순서가 없으며, 집합 연산(교집합, 합집합, 차집합)을 지원한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 추가
127.0.0.1:6379> SADD fruits "apple" "banana" "orange"
(integer) 3
127.0.0.1:6379> SADD fruits "apple"  # 중복 → 무시
(integer) 0

# 전체 조회
127.0.0.1:6379> SMEMBERS fruits
1) "apple"
2) "banana"
3) "orange"

# 개수
127.0.0.1:6379> SCARD fruits
(integer) 3

# 존재 확인
127.0.0.1:6379> SISMEMBER fruits "apple"
(integer) 1
127.0.0.1:6379> SISMEMBER fruits "grape"
(integer) 0

# 삭제
127.0.0.1:6379> SREM fruits "banana"
(integer) 1

집합 연산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> SADD setA "a" "b" "c"
127.0.0.1:6379> SADD setB "b" "c" "d"

# 교집합
127.0.0.1:6379> SINTER setA setB
1) "b"
2) "c"

# 합집합
127.0.0.1:6379> SUNION setA setB
1) "a"
2) "b"
3) "c"
4) "d"

# 차집합 (A - B)
127.0.0.1:6379> SDIFF setA setB
1) "a"

Set의 내부 구조

Set도 데이터 특성에 따라 인코딩이 달라진다.

1
2
정수만 저장하고 개수가 적을 때: intset
그 외: hashtable

intset 은 정수만 저장할 때 사용되는 매우 효율적인 구조다. 정렬된 정수 배열로 저장되어 이진 탐색으로 O(log N) 조회가 가능하다.


Sorted Set

Sorted Set은 Set처럼 중복 없음 + score로 정렬 되는 집합이다. 각 멤버에 score(점수)가 부여되어 자동으로 정렬된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 추가 (score, member)
127.0.0.1:6379> ZADD ranking 100 "kim"
127.0.0.1:6379> ZADD ranking 85 "lee"
127.0.0.1:6379> ZADD ranking 95 "park"

# score 순 조회 (오름차순)
127.0.0.1:6379> ZRANGE ranking 0 -1 WITHSCORES
1) "lee"
2) "85"
3) "park"
4) "95"
5) "kim"
6) "100"

# 내림차순
127.0.0.1:6379> ZREVRANGE ranking 0 -1 WITHSCORES
1) "kim"
2) "100"
3) "park"
4) "95"
5) "lee"
6) "85"

# 특정 멤버 순위 (0부터 시작)
127.0.0.1:6379> ZREVRANK ranking "kim"
(integer) 0  # 1등

# 특정 멤버 score 조회
127.0.0.1:6379> ZSCORE ranking "kim"
"100"

# score 증가
127.0.0.1:6379> ZINCRBY ranking 20 "lee"
"105"

Sorted Set의 내부 구조: Skiplist

데이터가 클 때 Sorted Set은 Skiplist + Hashtable 조합으로 구현된다.

Skiplist 는 계층화된 Linked List다.

1
2
3
4
Level 3:  1 ─────────────────────────→ 9
Level 2:  1 ──────────→ 5 ──────────→ 9
Level 1:  1 ───→ 3 ───→ 5 ───→ 7 ───→ 9
Level 0:  1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9

상위 레벨에서 빠르게 건너뛰고, 필요할 때 하위 레벨로 내려가서 탐색한다. 이를 통해 O(log N) 탐색이 가능하다.

Hashtable 은 member로 score를 빠르게 조회하기 위해 함께 사용된다.

1
2
3
4
5
6
Skiplist만 있으면:
ZSCORE ranking "kim" → O(log N) 탐색 필요

Skiplist + Hashtable:
member → score 매핑을 hashtable에 저장
ZSCORE ranking "kim" → O(1)

메모리는 더 쓰지만 조회 성능을 확보한다.

동점자 처리

score가 같으면 member의 사전순 으로 정렬된다.

1
2
3
4
5
6
127.0.0.1:6379> ZADD ranking 100 "kim" 100 "lee" 100 "park"
127.0.0.1:6379> ZRANGE ranking 0 -1
1) "kim"
2) "lee"
3) "park"
# k < l < p 순서


비트맵

비트맵은 비트(0/1) 단위로 데이터를 저장 한다. 사실 별도의 자료 구조가 아니라 String의 확장이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 특정 위치에 비트 설정
127.0.0.1:6379> SETBIT users:login:20241201 123 1
# 123번 유저가 12월 1일에 로그인했다!

127.0.0.1:6379> SETBIT users:login:20241201 456 1
# 456번 유저도 로그인

# 비트 조회
127.0.0.1:6379> GETBIT users:login:20241201 123
(integer) 1

127.0.0.1:6379> GETBIT users:login:20241201 789
(integer) 0

# 1인 비트 개수 (로그인한 유저 수)
127.0.0.1:6379> BITCOUNT users:login:20241201
(integer) 2

비트 연산

1
2
3
4
5
# 12월 1일, 2일 모두 로그인한 유저 (AND)
127.0.0.1:6379> BITOP AND both_days users:login:20241201 users:login:20241202

# 12월 1일 또는 2일에 로그인한 유저 (OR)
127.0.0.1:6379> BITOP OR any_day users:login:20241201 users:login:20241202

메모리 효율

유저 100만 명의 로그인 여부를 저장한다고 가정하자.

1
2
Set으로 저장: 100만 개 유저 ID 저장 → 수십 MB
Bitmap으로 저장: 100만 비트 = 125KB

주의: SETBIT으로 큰 오프셋을 설정하면 중간이 모두 0으로 채워져 메모리를 많이 사용할 수 있다.

1
2
SETBIT mykey 1000000000 1
# 10억 번째 비트 = 125MB 할당!


HyperLogLog

HyperLogLog는 대량 데이터의 고유 개수(cardinality)를 추정 하는 확률적 자료 구조다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 추가
127.0.0.1:6379> PFADD visitors "user1" "user2" "user3"
(integer) 1
127.0.0.1:6379> PFADD visitors "user1"  # 중복
(integer) 0

# 고유 개수 추정
127.0.0.1:6379> PFCOUNT visitors
(integer) 3

# 여러 HyperLogLog 합치기
127.0.0.1:6379> PFADD visitors:day1 "user1" "user2"
127.0.0.1:6379> PFADD visitors:day2 "user2" "user3"
127.0.0.1:6379> PFMERGE visitors:week visitors:day1 visitors:day2
127.0.0.1:6379> PFCOUNT visitors:week
(integer) 3

핵심 특징

  • 오차율: 약 0.81%
  • 메모리: 최대 12KB 고정
  • 데이터가 1억 개여도 12KB

동작 원리

HyperLogLog는 “드문 패턴이 나왔다 = 많이 시도했다” 는 확률적 원리를 이용한다.

1
2
3
4
5
6
7
데이터를 해시로 변환하면 0과 1로 된 비트열이 된다.
이 비트열에서 앞에 0이 연속으로 몇 개인지 관찰한다.

"user1" → 해시 → 000... → 0이 3개 연속 → 대략 2^3 = 8개
"user2" → 해시 → 0000... → 0이 4개 연속 → 대략 2^4 = 16개

0이 많이 연속될수록 더 많은 데이터가 있다고 추정한다.

16,384개의 버킷을 사용해 여러 번 측정하고 평균을 내서 정확도를 높인다.

Set vs HyperLogLog

1
2
3
4
5
6
7
8
9
고유 방문자 100만 명

Set: 수십 MB
HyperLogLog: 12KB

단, HyperLogLog는:
- 개별 요소 조회 불가 ("user1이 있는지?" → 확인 불가)
- 삭제 불가
- 정확한 값이 아닌 추정값

정확한 값 이 필요하면 Set, 대략적인 개수 만 필요하면 HyperLogLog를 사용한다.


Geospatial

Geospatial은 위치 데이터(경도, 위도)를 저장하고 조회 하는 자료 구조다. 내부적으로 Sorted Set을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# 위치 추가 (경도 위도 member)
127.0.0.1:6379> GEOADD travel 127.0016985 37.5642135 "seoul"
127.0.0.1:6379> GEOADD travel 14.399698 50.099242 "prague"
127.0.0.1:6379> GEOADD travel -122.434547 37.785303 "sanfrancisco"

# 위치 조회
127.0.0.1:6379> GEOPOS travel seoul
1) 1) "127.00169831514358521"
   2) "37.56421336758489099"

# 두 위치 사이의 거리
127.0.0.1:6379> GEODIST travel seoul prague KM
"8252.9957"


Stream

Stream은 로그성 데이터를 저장하고 소비 하는 자료 구조다. 카프카처럼 메시지 브로커로 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 메시지 추가 (* = 자동 ID 생성)
127.0.0.1:6379> XADD mystream * name "kim" action "login"
"1733840000000-0"

127.0.0.1:6379> XADD mystream * name "lee" action "purchase"
"1733840000001-0"

# 전체 조회
127.0.0.1:6379> XRANGE mystream - +
1) 1) "1733840000000-0"
   2) 1) "name"
      2) "kim"
      3) "action"
      4) "login"
2) 1) "1733840000001-0"
   2) 1) "name"
      2) "lee"
      3) "action"
      4) "purchase"

# 개수
127.0.0.1:6379> XLEN mystream
(integer) 2

List vs Stream

1
2
3
4
5
6
7
8
9
List (LPUSH + BRPOP):
- 메시지 꺼내면 사라짐
- 한 소비자만 받을 수 있음
- 처리 실패하면 메시지 유실

Stream:
- 메시지 계속 보관 (로그처럼)
- 여러 소비자 그룹이 같은 메시지 소비 가능
- 처리 실패해도 다시 읽을 수 있음

소비자 그룹

여러 소비자가 메시지를 나눠서 처리할 수 있다.

1
2
3
4
5
6
7
8
# 그룹 생성
127.0.0.1:6379> XGROUP CREATE mystream mygroup $ MKSTREAM

# 소비자가 메시지 가져가기
127.0.0.1:6379> XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >

# 처리 완료 알림
127.0.0.1:6379> XACK mystream mygroup 1733840000000-0


레디스에서 키를 관리하는 법

키의 자동 생성과 삭제

레디스는 키를 자동으로 생성하고 삭제 한다.

규칙 1: 키가 없으면 자동 생성

1
2
3
4
127.0.0.1:6379> DEL mylist
127.0.0.1:6379> LPUSH mylist "a" "b" "c"
(integer) 3
# mylist 키가 자동 생성됨

규칙 2: 마지막 아이템 삭제되면 키도 삭제

1
2
3
4
5
6
127.0.0.1:6379> LPUSH mylist "a"
(integer) 1
127.0.0.1:6379> LPOP mylist
"a"
127.0.0.1:6379> EXISTS mylist
(integer) 0  # 키가 사라짐

규칙 3: 키가 없는데 조회하면 빈 값 반환

1
2
127.0.0.1:6379> LRANGE mylist 0 -1
(empty array)  # 에러 안 남

주의: 타입이 다르면 에러

1
2
3
4
127.0.0.1:6379> SET hello "world"
OK
127.0.0.1:6379> LPUSH hello "a"
(error) WRONGTYPE Operation against a key holding the wrong kind of value


키와 관련된 커맨드

EXISTS

키 존재 여부를 확인한다.

1
2
3
4
5
127.0.0.1:6379> EXISTS mykey
(integer) 1  # 있음

127.0.0.1:6379> EXISTS key1 key2 key3
(integer) 2  # 존재하는 키 개수

KEYS

패턴으로 키를 조회한다.

1
2
3
4
5
6
7
8
127.0.0.1:6379> KEYS *
# 모든 키

127.0.0.1:6379> KEYS user:*
# user:로 시작하는 키

127.0.0.1:6379> KEYS user:???
# user: + 3글자

주의: 프로덕션에서 KEYS를 사용하면 안 된다.

모든 키를 스캔하므로 O(N)이며, 키가 많으면 레디스가 블로킹된다.

SCAN

커서 기반으로 키를 조회한다. KEYS와 달리 조금씩 나눠서 조회 하므로 안전하다.

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> SCAN 0
1) "17"        # 다음 커서
2) 1) "key1"   # 찾은 키들
   2) "key2"

127.0.0.1:6379> SCAN 17
1) "0"         # 0이면 스캔 완료
2) 1) "key3"

# 패턴 매칭과 개수 힌트
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 100

커서가 0으로 돌아오면 전체 스캔이 완료된 것이다.

1
2
3
4
5
# DEL: 동기 삭제
127.0.0.1:6379> DEL mykey

# UNLINK: 비동기 삭제
127.0.0.1:6379> UNLINK mykey

DEL 은 즉시 삭제하므로 큰 데이터면 블로킹이 발생한다. UNLINK 는 키와의 연결만 끊고 실제 메모리 해제는 백그라운드에서 처리한다. 큰 데이터를 삭제할 때는 UNLINK를 사용해야 한다.

EXPIRE와 TTL

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> SET session:123 "data"
OK
127.0.0.1:6379> EXPIRE session:123 3600
(integer) 1

127.0.0.1:6379> TTL session:123
(integer) 3598  # 남은 시간(초)

# 처음부터 만료 시간과 함께 저장
127.0.0.1:6379> SET session:123 "data" EX 3600

TTL의 내부 동작

TTL은 카운트다운 방식이 아니다. 만료될 시각(Unix timestamp)을 저장 해두고, 조회 시 현재 시각과 비교한다.

1
2
3
4
5
key: session:123
expire_at: 1733840060000

TTL 조회 시:
만료 시각 - 현재 시각 = 남은 시간

만료된 키는 두 가지 방식으로 삭제된다.

  1. Lazy 삭제: 키에 접근할 때 만료 여부를 확인하고 삭제
  2. Active 삭제: 백그라운드에서 주기적으로 샘플링하여 삭제 (매초 10번, 20개씩 샘플링)

두 방식을 조합하여 CPU 부하와 메모리 효율의 균형을 맞춘다.

RENAME

키 이름을 변경한다.

1
2
3
4
127.0.0.1:6379> RENAME oldkey newkey

# newkey가 없을 때만 변경
127.0.0.1:6379> RENAMENX oldkey newkey

TYPE

키의 자료 구조 타입을 확인한다.

1
2
3
4
5
6
7
8
127.0.0.1:6379> TYPE mystring
string

127.0.0.1:6379> TYPE mylist
list

127.0.0.1:6379> TYPE myset
set


마치며

레디스의 핵심 원칙을 정리하면 다음과 같다.

  1. 자료 구조 선택이 중요하다. 같은 데이터라도 어떤 자료 구조를 선택하느냐에 따라 메모리 사용량과 성능이 크게 달라진다.

  2. 내부 구현을 이해해야 한다. List는 Quicklist, Sorted Set은 Skiplist + Hashtable 등 내부 구현을 알아야 시간 복잡도를 예측하고 적절한 자료 구조를 선택할 수 있다.

  3. 블로킹 명령어를 피해야 한다. 레디스는 싱글 스레드이므로 KEYS, 큰 데이터의 DEL, HGETALL 등은 서비스 장애를 유발할 수 있다. SCAN, UNLINK, HSCAN 등의 대안을 사용해야 한다.

  4. 메모리는 유한하다. TTL 설정, 적절한 자료 구조 선택, maxmemory-policy 설정 등으로 메모리를 효율적으로 관리해야 한다.


References

  • REDIS FOR DEVELOPERS
This post is licensed under CC BY 4.0 by the author.