Redis 리스트(List)는 입력순서에 따라 정렬된 string의 목록이다. 목록의 왼쪽 혹은 오른쪽 끝에 새로운 요소를 밀어 넣는 식으로 리스트에 string 데이터를 추가 할 수 있다. 링크드리스트(Linked List)의 구현이라고 보면 된다.
LPUSH(Left push)는 왼쪽에 데이터를 추가하는 반면, RPUSH(Right Push)는 오른쪽에 데이터를 추가한다.
LPUSH와 RPUSH의 사용방법이다.
RPUSH key value [value ...]
LPUSH key value [value ...]
RPUSH를 이용해서 초기 데이터인 seoul, london, paris를 입력해보자.
RPUSH city seoul london paris
(integer) 3
city key에 3개의 value를 밀어넣었다. key의 값은 LRANGE로 읽을 수 있다.
LRANGE key start stop
start와 stop를 이용해서, 읽을 범위를 설정할 수 있다. 0부터 시작한다. stop에 -1을 설정하면 start에서 끝까지 데이터를 읽는다.
LRANGE city 0 3
1) "seoul"
2) "london"
3) "paris"
이제 위의 그림대로 "lisbon"과 "doha"를 넣어보자. 왼쪽끝에서 아이템을 밀어넣어야 하므로 LPUSH 명령을 이용해야 한다.
start에서 stop 범위에 있는 값을 자른다. 결과적으로 start ~ stop 범위의 value만 남는다.
RPOP key
리스트의 마지막에 있는 아이템을 읽고 삭제한다.
RPUSH key value [value ...]
리스트의 마지막에 하나 이상의 value를 추가한다.
RPUSHX key value
key가 존재할 경우, 리스트의 마지막에 value를 추가한다.
RPOPLPUSH source destination
source key로 부터 RPOP을 한다음, destination key에 RPOP한 아이템을 LPUSH 한다.
BRPOPLPUSH source destination
RPOPPLPUSH의 블럭 버전
Capped lists
리스트의 일반적인 시나리오 중 하나는 마지막 N개의 아이템을 저장하는 것이다. 실시간 메시징시스템을 만든다고 가정해 보자. 유저가 온라인 상태에서 메시지를 보내면 즉시 유저에게 전달될 것이다. 하지만 유저가 오프라인 상태 혹은 다른 이유로 메시지가 전달되지 않았다면, 이 메시지는 메시지 박스에 저장할 것이다. 유저가 오랜시간 오프라인 상태에 있을 수 있으니, 최근 메시지 N개만 남겨야 할 것이다. 이러한 종류의 리스트를 capped lists 라고 한다.
데이터베이스에서 메시지를 직접 읽는 대신에 Redis에서 읽는다. 보통 유저는 처음 N개만큼의 데이터를 주로 읽을 건데, 데이터를 RDBMS가 아닌 Redis에서 읽으므로 서비스 성능을 높일 수 있다. Redis에 저장된 데이터 N개를 초과해서 과거의 데이터를 보고 싶다면, 이때 RDBMS에서 데이터를 가져오면 된다. 유저 입장에서는 서비스 진입단계에서 메시지를 빠르게 확인 할 수 있으며, 과거의 데이터는 읽는 속도가 떨어져도 별 문제가 안되므로 체감 성능이 크게 좋아진다.
Capped List는 rpush와 ltrim으로 구현할 수 있다.
메시지가 들어올 때마다 TRIM을 하는 건 비효율적이므로 실제 구현을 할 때는 메시지 박스의 크기를 좀 더 크게 잡아서 TRIM 횟수를 줄이는 등의 방법을 써야 할 것이다. 예를 들어 100개의 최근 아이템을 저장하고 싶다면, 메시지 박스의 크기를 200으로 설정하고 200이 됐을 때, LTRIM을 이용 100개를 TRIM하는 식으로 구현 할 수 있을 것이다. LPUSH를 할 때 리스트의 전체 원소의 갯수를 반환하므로 이 반환 값을 비교해서 TRIM 여부를 결정하면 된다.
쇼핑카트 구현
쇼핑카트를 만들어보자. 먼저 쇼핑 프로세스를 정의해보자.
유저가 로그인한다. 로그인을 하면 세션을 발급할 것이다. 이 세션은 RDBMS와 Redis에 저장된다.
유저가 아이템을 쇼핑카드에 넣는다.
유저는 하나 이상의 아이템을 담을 수 있으므로 LIST 자료구조가 적당할 거다.
key를 만들고 아이템을 PUSH 한다. key는 session을 포함해서, 찾을 수 있도록 네이밍한다.
유저는 아이템을 뺄 수도 있다.
유저가 로그아웃하면 key를 삭제한다. 웹 애플리케이션의 경우 로그아웃하지 않고 웹 브라우저를 닫을 수 있으므로, key에 timeout을 정해야 할 것이다.
쇼핑카트를 구현하는 오래된 방법은 쿠키(cookie)에 저장하는 거다. 쿠키는 지금도 이런 목적으로 사용 할 수 있지만 모바일 애플리케이션에서 사용하기에는 좋은 방법이 아니다. 웹브라우저로만 대상으로 하는 웹 애플리케이션이 아니라면, 서버사이드에서 Redis로 해결하는게 더 좋은 방법이다.
yundream 유저의 세션 key가 "session-1234"라고 가정하자. 유저가 아이템을 구매하면 LPUSH로 아이템 ID를 밀어 넣는다. 쇼핑카트의 key 이름은 "cart:session"으로 정했다.
간단하게 해결 된 것 같지만 여러 정보를 저장한 JSON 형태로 저장을 했다면, 삭제가 어려워질 수 있다. JSON 데이터에 시간, 아이템 갯수등을 넣었다고 가정해보자. 일치하는 아이템을 넣어야 하는데, 불가능하지는 않겠지만 코드가 지저분해 질 것이다.
인덱스로 삭제 할 수 없을까 ? 왜 인지는 모르겠으나 Redis는 인덱스로 삭제하는 명령을 제공하지 않는다. 그래서 굳이 두 번의 명령으로 삭제를 해야 한다. 아래와 같은 아이템을 가진 리스트를 만들어보자.
Contents
LPUSH와 RPUSH로 데이터 밀어 넣기
POP으로 꺼내기
List 명령어들
Capped lists
쇼핑카트 구현
Recent Posts
Archive Posts
Tags