[Redis] 레디스와 캐시 그리고 데이터구조
이번에 회사에서 프로젝트를 맡으면서 redis를 접할기회가 생겼다. 근데 익숙하지 않은 데이터베이스라서 한번 정리해보려고 한다.
그리고 시작하기 전에 레디스를 살펴볼 gui툴로 medis도 설치했다.
2022.02.06 - Mac M1에서 가능한 Redis GUI Client 프로그램 : Medis2
지금까지 레디스를 단순히 캐쉬용으로 쓰이는 데이터베이스라고만 알고 있었다.
이번기회에 캐쉬용이 어떤건지, 그래서 레디스가 어떤 특징이 있는지 자세히 알아보려고 한다.
Cache
나중에 요청할 결과를 미리 저장해둔 후 빠르게 서비스해주는 것
즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 캐시에 접근해서 요청을 처리하는 것.
예를 들어, 캐쉬는 아래와 같은 flow로 동작한다.
- 클라이언트로부터 요청을 받는다.
- 캐쉬와 작업을 한다. -> 즉, 캐쉬에서 이미 들어왔던 작업인지 확인
- 실제 db와 작업한다 -> 캐쉬에 잇으면 캐쉬내용 리턴, 없으면 디비접근
- 다시 캐쉬와 작업한다 -> 디비접근했으니까 한번들어온거라서 캐쉬에 저장
Redis
Redis란 REmote DIctionary Server의 약자다. 직역하면 '원격 사전 서버'이다.
사전은 단어에 대한 의미를 보여주는 것인데, 즉 key(단어),value(의미)형태로 데이터가 저장되어 있는 원격서버라고 볼 수 있다.
- in-memory 데이터베이스로 read/write가 빠른 NoSQL
- 영속성(Persistence) 지원해서 서버가 꺼지더라도 다시 데이터를 불러들일 수 있다.
- key-value 형태로 데이터를 저장해서 별도의쿼리 없이 key로 value를 찾을 수 있다.
- Single Thread로 원자성(Atomic)을 보장하기 때문에 Race Condition 발생가능성이 낮다.
- 마스터-슬레이브 아키텍쳐를 사용해서 비동기식 복제지원 -> 슬레이브 서버에 복제될 수 있어서 분산지원
- 데이터구조(Collection) : String, List, Set, Sorted Set, Hash
미리 읽어두었다가 요청이 올 경우 빠르게 응답 하기 위한 '캐쉬'용으로 사용할 수 있다.
이 경우 전체 데이터가 필요없고, 데이터가 항상 유지될 필요가 없기 때문에 redis 같은 적은 공간의 데이터베이스가 최적이다.
또한 메모리기반의 디비이기 때문에 get,put을 이용해서 데이터를 넣고 뺄 수 있다.
redis의 경우 value가 단순한 object가 아니라 여러가지 자료구조를 갖는게 특징이다.
Redis 자료구조
String
단순한 key-value 1:1 매핑구조
- get : key에 해당하는 value 리턴
- set : key에 value저장
- del : key 삭제
명령어 사용 예시 보기
127.0.0.1:6379> get a //key'a'의 value조회
(nil)
127.0.0.1:6379> set a b //key'a'에 value로 'b' 저장
OK
127.0.0.1:6379> get a //key'a'의 value조회
"b"
127.0.0.1:6379> del a //key'a' 삭제-> 삭제한 갯수 반환
(integer) 1
List
순서가 있는 String의 묶음
array 형식의 데이터구조 (자바의 linked list와 유사)
처음(first)과 끝(last)에 데이터 삽입/삭제는 빠르지만, 중간에 데이터를 삽입할때 어려움
- lpush <key> <value> : left 즉, 첫번째(index:0)에 데이터삽입
- rpush <key> <value> : right 즉, 마지막(index:-1)에 데이터삽입
- lpop <key> <value> : left 즉, 첫번째(index:0)에 데이터 삭제
- rpop <key> <value> : right 즉, 마지막(index:-1)에 데이터 삭제
- lrange <key> <s> <e> : s부터 e index의 데이터 반환
명령어 사용 예시 보기
127.0.0.1:6379> lpush a 1 # 추가된 value 갯수 리턴
(integer) 1
127.0.0.1:6379> rpush a 2
(integer) 2
127.0.0.1:6379> lpush a 3
(integer) 3
127.0.0.1:6379> lpush a 4
(integer) 4
127.0.0.1:6379> rpush a 5
(integer) 5
127.0.0.1:6379> lrange a 0 -1 # 첫 번째 인덱스 : 0 / 마지막 인덱스 : -1 / 마지막에서 2번째 인덱스 : -2...
1) "4"
2) "3"
3) "1"
4) "2"
5) "5"
127.0.0.1:6379> lrange a 0 3
1) "4"
2) "3"
3) "1"
4) "2"
127.0.0.1:6379> llen a
(integer) 5
127.0.0.1:6379> rpop a 3 # 끝에서 3개 반환
1) "5"
2) "2"
3) "1"
Sets
순서가 없는 String의 묶음
중복되지 않은 array 형식의 데이터구조 , Set의 value는 member
set 간의 연산을 지원해서 교집합,합집합, 차이를 뽑아낼수 있다.
- sadd <key> <value> : key에 value(member)삽입
- srem <key> <value> : value(member)삭제
- smembers <key> : 모든 value (member)조회
- scard <key> : member count 조회
- spop <key> : 무작위로 member 삭제
명령어 사용 예시 보기
127.0.0.1:6379> sadd myset a # 추가된 member 갯수 반환
(integer) 1
127.0.0.1:6379> sadd myset a
(integer) 0
127.0.0.1:6379> sadd myset b
(integer) 1
127.0.0.1:6379> sadd myset c
(integer) 1
127.0.0.1:6379> srem myset c # 삭제된 member 갯수 반환
(integer) 1
127.0.0.1:6379> smembers myset
1) "b"
2) "a"
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> sadd myset c d e f # 여러 member 삽입 가능
(integer) 4
127.0.0.1:6379> smembers myset
1) "d"
2) "c"
3) "a"
4) "f"
5) "b"
6) "e"
127.0.0.1:6379> spop myset 3 # 랜덤 member 삭제
1) "d"
2) "c"
3) "f"
127.0.0.1:6379> smembers myset
1) "a"
2) "b"
3) "e"
Sorted Set
set과 마찬가지로 순서가 없는 String의 묶음
하지만 score를 통해 순위를 매겨 정렬이 가능하다. (오름차순) , Set의 value는 member
- score값이 같으면 value로 정렬한다.
- value는 중복이 불가능하지만, score값은 중복이 가능하다.
score값은 일종의 가중치로 볼 수 있고,
정렬이 되어 있기 때문에 score값 범위에 따른 쿼리(range query), top rank에 따른 쿼리가 가능하다.
- zadd <key> <score> <member> : key에 score와 member 추가
- zcard <key> : member count 조회
- zrange <key> <s> <e> : s부터 e까지 index의 member 반환
- zrangebyscore <key> <min> <max> : min부터 max까지 해당하는 scroe값을 갖는 member 반환
- zrem <key> <member> : member 삭제
명령어 사용 예시 보기
127.0.0.1:6379> zadd ss 5 e # 추가된 value 갯수
(integer) 1
127.0.0.1:6379> zadd ss 3 c
(integer) 1
127.0.0.1:6379> zadd ss 1 a
(integer) 1
127.0.0.1:6379> zadd ss 2 b
(integer) 1
127.0.0.1:6379> zadd ss 4 d
(integer) 1
127.0.0.1:6379> zcard ss # value count
(integer) 5
127.0.0.1:6379> zrange ss 0 -1 //처음부터 끝까지 'ss'key의 member 반환
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
127.0.0.1:6379> zrangebyscore ss 2 4 //index 2부터 4까지 'ss'key의 member 반환
1) "b"
2) "c"
3) "d"
127.0.0.1:6379> zrem ss b # 삭제된 value 갯수
(integer) 1
127.0.0.1:6379> zrange ss 0 -1 //삭제된 후, 처음부터 끝까지 'ss'key의 member 반환
1) "a"
2) "c"
3) "d"
4) "e"
Hash
key내부에 key-value가 존재하는 자료구조
key의 Filed는 subkey로 갯수에 제한이 없다.
여러가지 object타입을 저장하기 좋고, 하나의 key에 약 40억개의 key-value 저장가능
- hset <key> <field> <value> <field> <value> .. : 하나의 key에 여러 field-value 저장
- hget <key> <field> : key와 field에 해당하는 value 리턴
- hdel <key> <field> : field 삭제
- hlen <key> : field count 리턴
- hgetAll <key> : 모든 field-value 쌍 반환
- hkeys <key> : 모든 field 리턴
- hvals <key> : 모든 value 리턴
명령어 사용 예시 보기
# field - value : name - jinmin / year - 1995 / month - 3
127.0.0.1:6379> hset hh name jinmin year 1995 month 3
(integer) 3
127.0.0.1:6379> hget hh name
"jinmin"
127.0.0.1:6379> hget hh year
"1995"
127.0.0.1:6379> hdel hh year
(integer) 1
127.0.0.1:6379> hlen hh
(integer) 2
127.0.0.1:6379> hgetAll hh
1) "name"
2) "jinmin"
3) "month"
4) "3"
127.0.0.1:6379> hkeys hh
1) "name"
2) "month"
127.0.0.1:6379> hvals hh
1) "jinmin"
2) "3"
참고
https://sabarada.tistory.com/103