[Redis] 레디스 기본 개념, 자료형
태그: Redis
카테고리: Redis
- 해당 포스트는 인프런 강의를 듣고 정리한 글입니다.
- redis 사용 시 명령어 참고용 포스트
1. Redis 알아보기
Redis 란 Remote Dictionary Server
를 줄여서 부르는 말이다. 레디스는 분산서버 환경에 있는 서버가 공통으로 사용할 수 있는 해시 테이블이라고 생각하면 된다.
Dictionary 라는 의미는 말그대로 ‘사전’ 이라는 뜻인데, HashMap
처럼 key-value 형태로 O(1) 의 시간복잡도로 사용이 가능하다는 의미이다.
레디스는 인메모리 데이터 스토어다. 인메모리라는 말은 데이터를 램에 저장한다는 의미다. 일반적으로 램은 디스크에 비해 빠르기 때문에 관계형 데이터베이스와 다른 구조와 특징을 갖는다.
Redis 특징
- In-Memory : 백업을 제외한 모든 데이터를 램에 저장
- Single Thread : 단일 스레드를 사용하여 복잡도를 낮추고 성능을 향상
- Cluster Mode : 다중 노드에 데이터를 분산 저장
- Persistence : 주로 휘발성 데이터를 저장하기 위해 레디스를 사용하지만, RDB(Redis Database) 와 AOF(Append only file)을 통해 영속성 옵션 제공
- Pub/Sub : 실시간 채팅, 알림 서비스와 같은 기능을 쉽게 구현 가능
Redis 장점
- 높은 성능 : 데이터를 메모리에 저장하기 때문에 빠른 읽기/쓰기 속도
- Data Type : Redis 는 다양한 데이터 타입을 지원한다. 이를 활용하여 다양한 기능 구현 가능
- 클라이언트 라이브러리 지원 : Python, Java, JavaScript 등 다양한 언어로 작성된 클라이언트 라이브러리 지원. 백엔드와 쉽게 연동 가능
Redis 사용 사례
- Caching : 임시 비밀번호(OTP), 로그인 세션과 같은 1회성 데이터를 레디스에 캐싱하여 사용
- Rate Limiter : 서버에서 특정 API 의 요청 횟수를 제한하기 위한 용도
- Message Broker : 레디스의
Streams
,list
와 같은 데이터 타입을 이용하여 메시지 브로커 구현 - 실시간 분석/계산 : 랭킹, 반경탐색, 방문자 수 계산
- 실시간 채팅 :
pub/sub
패턴을 활용
Persistence
- 영속성 : 레디스는 주로 캐시로 사용되지만 데이터 영속성을 위해 SSD 와 같은 영구 저장장치에 데이터를 저장할 수 있는 옵션을 제공한다.
- RDB(Redis Databases) : Point-in-time Snapshot. 재난복구 또는 복제에 사용. 일부 데이터 유실의 위험이 있고, 스냅샷 생성 중 클라이언트 요청 지연 발생
- AOF(Append Only File) : 레디스에 적용되는 쓰기 작업을 모두 log로 저장. 데이터 유실의 위험이 적지만, 재난 복구 시 쓰기 작업을 다시 적용하기 때문에 RDB 보다 느림
Caching
데이터를 빠르게 읽고 처리하기 위해 임시로 저장하는 기술. 계산된 값을 임시로 저장해 동일한 계산이나 요청이 필요할 때 다시 계산하지 않고 빠르 결과값을 이용하기 위해 사용.
- 웹 브라우저 캐싱 : HTML, CSS, 이미지 등 자주 바뀌지 않는 데이터를 캐싱해 둔다.
- DNS 캐싱 : 도메인과 ip 를 캐싱해 두고 요청 시 빠르게 응답한다.
- 데이터베이스 캐싱 : MySQL 과 같은 데이터베이스는 버퍼풀 이라는 곳에 자주 사용되는 데이터를 메모리에 미리 올려놓고 사용한다.
- CDN : 이미지나 동영상 등 파일 크기가 큰 데이터를 POP 서버에 미리 옮겨두고 사용자가 요청시 가장 가까운 POP 서버에서 응답한다
Cache Hit / Miss
- Cache Hit : 캐시가 존재하여 정상적으로 리턴된 상황
- Cache Miss : 캐시 데이터가 없어서 아무것도 리턴되지 않은 상황
Cache-Aside pattern
사용자 요청에 대해 어플리케이션에서 Cache hit 인 경우 캐시에서 조회하고 cache miss 인 경우 원본 스토리지에서 데이터를 가져온 다음 나중에 사용할 수 있도록 캐시에 저장한 후 반환하는 패턴. 캐시를 사용하는 패턴 중 가장 흔한 형태. 다음과 같은 순서로 동작한다.
- cache miss 감지
- 데이터 가져오기
- 캐시 가져오기
- 데이터 반환
2. Redis 의 Data Type 알아보기
Strings
문자열, 숫자, serialized object(JSON string) 등 저장할 때 사용.
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# fruit 라는 key 에 apple 이라는 value를 넣겠다
$ SET fruit apple
# 한번에 여러개 저장
$ MSET price 100 language ko
# 한번에 여러개 반환
$ MGET fruit price language
# 숫자형 데이터 1증가
$ INCR price
# 10 증가
$ INCRBY price 10
# JSON string 데이터 저장 (stringify 되어 저장되기 때문에 사용할 때는 다시 parsing 해서 사용해야한다.)
$ SET box '{"price": 100, "type": "banana"}'
# 키를 만들 때 ':' 을 사용하여 만든다.
$ SET fruit:apple:price 200
Lists
String 을 linked list 로 저장한다. 양 끝에 데이터를 추가, 삭제하는 push/pop 에 최적화 O(1). message broker 구현 시 사용할 수 있다.
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 큐 구현 (왼쪽에서부터 넣고 오른쪽에서 부터 꺼냄)
$ LPUSH queue job1 job2 job3
$ RPOP queue
# 스택 구현 (왼쪽에서부터 넣고 왼쪽에서 부터 꺼냄)
$ LPUSH stack job1 job2 job3
$ LPOP stack
# 왼쪽에서 카운트 시 맨 마지막 인덱스는 -1
# 뒤에서 두번째 ~ 첫번째 아이템 조회 (맨 먼저 추가된 아이템 두개 조회)
$ LRANGE queue -2 -1
# 맨 첫번째부터 맨 마지막까지 조회
$ LRANGE queue 0 -1
# 작업 후 남길 인덱스 지정
$ LTRIM queue 0 0 # 0번 인덱스만 남기는 경우
Sets
집합. 유니크한 String 저장 시 사용가능.
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# user1 의 fruits key 에 apple, banana, orange 추가 (orange 중복으로 들어가지 않음)
$ SADD user:1:fruits apple banana orange orange
# user1 fruits 항목 조회
$ SMEMBERS user:1:fruits
# cardinality 측정
$ SCARD user:1:fruits
# banana 가 user:1:fruits 에 있는지 확인 (S is ~)
$ SISMEMBER user:1:fruits banana
# user1 과 user2 의 교집합 출력
$ SINTER user:1:fruits user:2:fruits
# 차집합: user1 - user2
$ SDIFF user:1:fruits user:2:fruits
# 합집합
$ SUNION user:1:fruits user:2:fruits
Hashes
field-value 구조를 갖는 데이터 타입. Map
과 유사하다. 다양한 속성을 갖는 객체의 데이터 저장시 유용
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# java 에서 해당 코드와 같다.
# String key = "lecture";
# redisHashes.putIfAbsent(key, new HashMap<>());
#
# redisHashes.get(key).put("name", "redis");
# redisHashes.get(key).put("price", "100");
# redisHashes.get(key).put("language", "ko");
$ HSET lecture name redis price 100 language ko
# 조회
$ HGET lecture name
# 다수 조회 (invalid 는 없는 항목이라 조회안됨(nil))
$ HMGET lecture price language invalid
# lecture key 의 price 10 증가
$ HINCRBY lecture price 10
Sorted Sets
Set의 기능에 추가로 score 속성을 저장한다. score 값에 따라 정렬 유지한다. score 가 동일한 경우 사전편찬 순으로 정렬. 랭킹 구현시 사용 가능
명령어
1
2
3
4
5
6
7
8
9
10
11
12
# TeamA, TeamB, TeamC 순으로 저장된다.
$ ZADD points 10 TeamA 10 TeamB 50 TeamC
# list 와 마찬가지로 range 명령어로 조회가능. (0 -1 : 처음 인덱스부터 끝 인덱스까지 조회)
$ ZRANGE points 0 -1
# 역순으로 score 와 함께 반환
$ ZRANGE points 0 -1 REV WITHSCORES
# TeamA 의 랭킹 반환 (인덱스 반환)
$ ZRANK points TeamA # 0
Streams
append-only log 에 consumer groups 기능을 더한 자료 구조. 항상 새 데이터가 끝에 추가되는 추가 전용 데이터 구조.
unique id 를 통해 하나의 entry 를 읽을 때, O(1) 시간복잡도를 가진다. consumer group 을 통해 분산 시스템에서 다수의 consumer가 event 처리.
vs. list 자료구조를 통해 message broker 구현
List | Stream | |
---|---|---|
순서 | 삽입 순서 유지. 추가된 순서대로 소비 | 삽입 순서 유지. 트정 id 부터 읽을 수 있는 기능 있음 |
확인 | 메시지의 확인을 지원하지 않음 | 컨슈머 그룹을 통해 메시지 확인을 지원. 신뢰할 수 있는 메시지 전달과 오류 허용성 제공 |
consumer groups | x (메시지 소비 상태를 관리하지 않음) | o (소비자가 독립적으로 동일한 스트림에서 읽고 메시지 소비 상태를 유지할 수 있음) |
메시지 영속성 | 주로 메모리에 저장 | 디스크에 저장하는 옵션 제공. 재시작해도 메시지 유지 가능 |
메시지 보존 | 메시지가 소비되면 리스트에서 삭제됨 | 시간이나 최대 길이와 같은 기준에 따라 메시지를 자동으로 만료하거나 삭제가능 |
확장성 | 리스트 조작 시 병목현상 발생 가능 | 메시지 파티셔닝 기능 제공 |
명령어
1
2
3
4
5
6
7
8
9
10
11
# Stream 에 entry 추가 - key 이후에 * (현재시간을 의미) 를 통해 unique id 할당
# events 라는 스트림에 "action":"like", "user_id":"1", "product_id":"1" 추가
$ XADD events * action like user_id 1 product_id 1 # 1번 유저가 1번 상품에 좋아요 누름
$ XADD events * action like user_id 2 product_id 1 # 2번 유저가 1번 상품에 좋아요 누름
# message 조회 - 가장 처음에 들어간 내용부터 마지막 내용까지 조회
$ XRANGE events - +
# id 값에 해당하는 유저의 이벤트 삭제
$ XDEL events 169206801584-0
아이디어
1번 유저가 1번 상품을 좋아요 누른 것을 확인 후 1번과 관련된 상품을 추천해 주거나 구매 유도를 위해 할인쿠폰 지급
Geospatial
좌표를 저장하고, 검색하는 데이터 타입. 거리 계산, 범위 탐색 등 지원.
명령어
1
2
3
4
5
6
7
8
# 강남역과 홍대역 좌표 저장
$ GEOADD seoul:station
126.923917 37.556944 hong-dae # 경도 위도
127.027583 37.497928 gang-nam
# 두 역의 거리 계산 Km
$ GEODIST seoul:station hong-dae gang-nam KM
Bitmaps
String 타입에 binary operation 을 적용한 것. 매우 적은 데이터로 저장하기 때문에 효육적이다.
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 로그인 내역을 기록할 때 사용가능
# 1월 1일에 123번, 456번 유저가 로그인했다.
$ SETBIT user:log-in:23-01-01 123 1 # 1 = true, 0(default) = false
$ SETBIT user:log-in:23-01-01 456 1
# 1월 2일에 123번 유저가 로그인 했다.
$ SETBIT user:log-in:23-01-02 123 1
# 1월 1일에 로그인한 유저 수 조회
$ BITCOUNT user:log-in:23-01-01
# AND 연산의 조건 검사 결과를 result 에 담는다
$ BITOP AND result
user:log-in:23-01-01 user:log-in:23-01-02
# result 에서 123 번 유저가 있다면 1 출력 없다면 0 출력
$ GETBIT result 123
HyperLogLog
집합의 cardinality 를 추정할 수 있는 확률형 자료구조. 정확성을 포기하는 대신 저장공간을 효율적으로 사용. 해시값으로 저장하기 때문에 해시충돌 가능성 있다.
vs. Set
Set 은 실제 값을 저장하므로 아이템 갯수에 비례해 저장공간 증가함. 반면에 HyperLogLog 는 실제 값을 저장하지 않기 때문에 아이템을 다시 조회 불가능.
MEMORY USAGE key
명령어로 메모리 사용량 확인가능.
명령어
1
2
3
4
5
6
7
# fruits 에 항목 추가
$ PFADD fruits apple orange grape kiwi
# 중복도 출력
$ PFCOUNT fruits # 4 --> 그러나 확률적이다. 갯수가 많아지면 다르게 나올 가능성있음
3. 특수 명령어
데이터 만료(Expiration)
데이터를 특정시간 이후에 만료 시키는 기능. 데이터 조회 시 만료된 데이터는 조회되지 않음. 만료되자마자 삭제하는 것이 아니라 백그라운드에서 주기적으로 만료 표시된 항목 삭제함.
명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ SET greeting hello
# 만료시간 설정 (10초)
$ EXPIRE greeting 10
# 만료시간 조회
$ TTL greeting # -1 : 만료시간 설정되지 않은 상태, -2 : 만료됨
$ GET greeting
# 한줄로 설정
$ SETEX greeting 10 hello
SET NX/XX
- NX : 해당 키가 존재하지 않는 경우에만 SET (set if not exist, INSERT)
- XX : 해당 키가 이미 존재하는 경우에만 SET (set if exist, UPDATE)
명령어
1
2
3
4
$ SET greeting hello NX # hello 저장됨
$ SET greeting hi XX # hi 로 변경됨
Pub/Sub
publisher 와 subscribe가 서로 알지 못해도 통신이 가능하도록 decoupling 된 패턴. 서로 직접 메시지를 주고받지 않고 채널에 발행한다. 구독자는 관심있는 채널을 필요에 따라 구독하여 메시지를 수신한다.
vs. Stream
Stream | Pub/Sub | |
---|---|---|
통신 패턴 | 생산자가 스트림에 메시지를 푸시하고 소비자가 스트림에서 메시지를 소비(메시지 큐) | 일대다 메시징 패턴. 발행자는 채널에 메시지 발행, 구독자는 채널에서 메시지 수신 |
메시지 지속성 | 메시지가 디스크에 지속되면 구독자가 오프라인일 때도 메시지 검색 가능 | 메시지 지속 x. 구독자는 메시지를 수신하려면 활성 상태로 연결되어 있어야한다. 연결이 끊어진 동안 발행된 메시지를 수신 불가 |
메시지 순서 | 순서 유지. 소비자는 스트림에 추가된 순서대로 메시지 읽음 | 순서 보장하지 않음. 구독자는 메시지가 발행되는대로 받음. |
컨슈머 그룹 | 컨슈머 그룹 지원. 여러 소비자가 독립적으로 스트림에서 읽고 소비상태 유지 가능 | 컨슈머 그룹 없음. 구독자는 그룹화 되지 않으며, 각 구독자는 채널에 발행된 각 메시지를 수신 |
확인 | 메시지 확인 지원 | 메시지 확인 불가 |
명령어
1
2
3
4
5
6
# 채널 구독한다. 구독 채널에 메시지 발행되면 자동으로 수신한다.
$ SUBSCRIBE ch:order ch:pay
# ch:order 에 메시지를 발행한다.
$ PUBLISH ch:order new-order
Pipeline
다수의 명령어를 한번에 요청하여 네트워크 성능을 향상.
예시
1
2
3
4
5
6
$ SET key value
$ EXPIRE key 60
$ SET & EXPIRE # 한번 요청으로 네트워크 횟수를 줄인다
Transaction
다수의 명령을 하나의 묶음으로 처리. 원자성 보장. 에러 발생시 모든 작업 롤백.
명령어
1
2
3
4
5
6
7
8
9
10
11
# 트랜잭션 시작
$ MULTI
# 비즈니스 로직 실행
$ INCR foo
# 롤백
$ DISCARD
# 커밋
$ EXEC
댓글남기기