Publish:

태그:

카테고리:

  • 해당 포스트는 인프런 강의를 듣고 정리한 글입니다.
  • 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

img

  • Cache Hit : 캐시가 존재하여 정상적으로 리턴된 상황
  • Cache Miss : 캐시 데이터가 없어서 아무것도 리턴되지 않은 상황

Cache-Aside pattern

사용자 요청에 대해 어플리케이션에서 Cache hit 인 경우 캐시에서 조회하고 cache miss 인 경우 원본 스토리지에서 데이터를 가져온 다음 나중에 사용할 수 있도록 캐시에 저장한 후 반환하는 패턴. 캐시를 사용하는 패턴 중 가장 흔한 형태. 다음과 같은 순서로 동작한다.

  1. cache miss 감지
  2. 데이터 가져오기
  3. 캐시 가져오기
  4. 데이터 반환

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 구현 시 사용할 수 있다.

명령어

img

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

reference

방문해 주셔서 감사합니다! 댓글,지적,피드백 언제나 환영합니다😊

댓글남기기