brunch

You can make anything
by writing

C.S.Lewis

by 에디의 기술블로그 Jul 07. 2019

레디스 List, Sorted Set 자료형

스프링부트 환경에서 레디스 List, Sorted Set 자료형 사용

레디스는 다양한 데이터 자료구조를 제공한다. 


String

List

Hashes

Set

Sorted Set


이글에서는 List 와 Sorted Set 에 대해서 소개할 예정인데 레디스에 대해서 잘 아는 개발자는 이 글은 안봐도 된다. 레디스를 처음 접하는 개발자에게 추천하는 글이다. 샘플코드는 필자에게 익숙한 스프링부트 환경에서 작성하였다. 하지만, 코드를 읽는 것이 어렵지는 않을 것이다. 이 글에서 스프링부트는 중요하지 않고, 레디스 자료구조를 이해하는 것이 더 중요하다. 잘못된 내용은 피드백을 해주길 바란다. 


필자의 글에서 소개한 사례는, "이것이레디스다" 라는 책에 유사한 내용이 있다. 물론, 샘플코드는 해당 책의 코드와 전혀 다르지만, 개념은 거의 똑같기 때문에 참고하기 좋을 것이다. 또한, 비슷한 사례를 실무에서 많이 찾을 수 있다. 포털, 커머스 등 대부분의 웹서비스 플랫폼에서 레디스를 비슷한 용도로 사용할 수 있다. 


암튼, 조금 오래된 책이긴 하지만, 레디스에 대해서 잘 알고 싶은 개발자는 "이것이 레디스다"라는 책을 꼭 읽어보길 바란다. 


UseCase


포털사이트 활용 사례를 생각해보자.  


요구사항

인터넷 포털 사이트의 방문 기록을 저장해야 하는 기능을 구현해야 한다. 로그인 아이디를 키 값으로 해서 최근 방문한 10개의 사이트 URL 을 저장한다. 해당 데이터는 "최근 읽은 글" 이라는 기능으로 고객에게 제공할 것이다. 

 

NoSQL 이 아니라면?

인터넷 포털사이트 방문 기록은 RDBMS에 저장 될 것이다. 사용자의 아이디를 "WHERE id = " 조건으로 SELECT 쿼리로 조회할 수 있다. RDBMS를 사용하면 아주 심플하게 구현할 수 있지만, 사용자가 많아지고 방문기록이 쌓일수록 조회 성능이 떨어질 것이다.


NoSQL, Redis 를 활용해보자.

Redis 는 Key-Value 기반의 NoSQL 오픈소스이다. 사용자의 방문 기록을 저장하는 역할이라면 Redis 로 매우 적합할 것이다. 하지만, 복잡한 기능을 구현해야 한다면 Redis 는 적합하지 않을수도 있다. NoSQL 을 사용해서 모든 것을 구현하겠다는 생각은 하지 말자. 



Redis List, Spring Boot Data Redis


레디스의 List 자료형을 사용해서 구축해보자. 사용자의 방문기록을 레디스 의 List 자료형에 저장한다.


Redis List 란?

레디스 List 자료구조의 특징은 다음과 같다.

Lists는 key와 value가 일 대 다 관계이다.

value는 입력된 순서대로 저장된다.

Lists는 주로 큐(Queue)와 스택(Stack)으로 사용된다.


LPUSH 명령어는 리스트의 왼쪽에 데이터를 저장하는 명령어이다. 시간복잡도는 O(1) 이다. 

http://redisgate.kr/redis/command/lpush.php

간단하게 데이터를 입력하고 테스트를 해보자. 

mylist 키에 저장된 데이터는 아래와 같다. LPUSH 명령어를 사용했기 때문에 왼쪽에 데이터가 추가되었다. 그래서 가장 나중에 추가된 "d"가 가장 왼쪽에 위치한다. 


d    -    c    -    b    -    a

방문기록을 레디스의 List 자료형에 왼쪽에 저장한다.

포털사이트 방문기록을 레디스의 List 자료형에 추가하는데, 새로운 데이터를 왼쪽에 추가한다. 즉, 가장 최근의 방문기록이 가장 왼쪽에 위치할 것이다. 참고로 스프링부트 환경에서 spring-data-redis의 ListOperations 사용한다. 키 설계는 아래와 같다. 

articles:사용자ID

ListOperations 의 leftPush 메서드로 왼쪽에 데이터(방문기록)을 추가한다. 단, 최대 값 이상 저장되면 최근 입력한 데이터만 남겨놓고 나머지는 지우는 로직을 추가하였다. 방문기록을 10개만 남기고 싶다면 ARTICLE_MAX_SIZE 를 10 으로 설정하면 된다. 테스트 코드를 돌려서 샘플 데이터가 저장되는지 확인해보자. 

레디스 콘솔에서 확인해보면 아래와 같이 잘 저장된 것을 확인할 수 있다. 

1) 에 표시된 데이터가 가장 나중에 저장된 데이터이다. 즉, 가장 왼쪽 데이터이고, 요구사항을 따르면 가장 최근에 방문한 방문기록이 되는 것이다. 


최근 방문기록 5개를 조회해보자. 

조회 역시 spring-data-redis 의 기능을 사용하겠다. ListOperations 의 range 메서드를 사용하면 된다. 

테스트 코드를 돌려서 정상적으로 데이터를 가져오는지 확인해보자. 

정상적으로 잘 되는 것을 확인할 수 있다. 


하지만, 작은 이슈가 발생하였다. 


이미 방문했던 페이지를 한번 더 방문하게 되면 어떻게 될까?? 

기존에 방문했던 URL을 저장해보자. 

기존 저장 데이터는 아래와 같다. 가장 최근 방문 기록은 "://url/5" 이고, 가장 오래 전 방문은 "://url/1" 이다. 

변경 저장 데이터는 아래와 같다. 최대 5개만 저장하고 지우도록 구현했기 때문에 "://url/1" 는 지워졌다. 대신 신규로 "://url/3" 이 가장 왼쪽에 추가 되었다. "://url/3" 는 2개가 존재하는 상황이다. 

필자가 원하는 요구사항은 중복 데이터는 가장 최근 기록만 남기도록 하는것이다. 이 요구사항을 구현하기 위해서는 어떻게 해야할까?

 

해결하기 위해서...

신규 데이터를 저장하기 전에, 기존 데이터와 중복 여부를 체크해야 한다.

방문기록을 저장하기 전에... 즉, 레디스의 List 자료형의 왼쪽에 데이터를 추가하기 전에, 중복 데이터를 확인해야 한다. List 자료형 데이터를 먼저 가져와서 중복 체크하고 중복 데이터가 존재한다면 기존 중복 데이터를 삭제하고 신규데이터를 추가해야 한다. 코딩이 조금 더 복잡해질 것이고, 성능이 안좋아질 것이다. 


이런 요구사항이라면... 레디스 List 자료형을 쓰지 않는게 좋을 것 같다...


https://github.com/sieunkr/spring-data/tree/master/spring-data-redis-list


해당 요구사항을 해결하기 위해서는 레디스의 Set 자료형을 사용해야 한다.


Sorted Set, Spring Boot Data Redis


중복 문제를 해결하기 위해서 레디스 Set 이 적합하지만, 그냥 Set 자료형은 정렬이 되지 않는다. 그래서, score 기준으로 정렬이 가능한 Sorted Set 자료구조를 검토해보자. List 처럼 사용할 수 있으면서, 중복체크도 되고, 정렬도 된다는 장점이 있다. 


Redis Sorted Set

레디스 Sorted Set 의 특징은 다음과 같다.

Sorted Sets는 key 하나에 여러개의 score와 value로 구성된다.

Value는 score로 sort되며 중복되지 않는다.

score가 같으면 value로 sort된다.

Sorted Sets은 주로 sort가 필요한 곳에 사용된다.


방문기록을 레디스의 Sorted Set 자료형에 저장한다. 

스프링부트 환경에서 spring-data-redis의 ZsetOperations 사용한다. 키 설계는 List 에서 사용했던 설계와 같다. 

참고로, removeRange 를 사용한 곳에서 음수형 파라미터를 넣었는데, 관련해서는 필자가 설명할 자신이 없다. "이것이 레디스다" 라는 책에 설명이 잘 나와있다. 


테스트 코드를 작성하여 데이터를 입력해보자. 

저장된 데이터를 레디스 콘솔에서 확인해보자. zrange 명령어를 사용했기 때문에 데이터는 오름차순으로 조회가 되었다. 

필자는 스코어에 TimeStamp 를 저장하였기 때문에, 오래 전에 입력한 데이터가 1순위로 조회될 것이다. 만약, 필자의 요구사항을 반영하기 위해서는, zrange 가 아니라, zrevrange 명령어를 사용해야 한다. 


최근 방문기록 5개를 조회해보자. 

Sorted Set 데이터에서 스코어 기준으로 오름차순 또는 내림차순으로 정렬되기 때문에, 조회할 때 주의가 필요하다. 아무생각없이 오름차순으로 조회를 하면 최근 방문기록이 아니라, 가장 오래전에 방문했던 기록이 조회될 수도 있다. Spring Data Redis 에서는 ZSetOperations 의 reverseRange 라는 메서드를 사용하면 된다. 

해당 메서드를 사용해서 구현하면 아래와 같다. 

end 에 -1 을 넣으면 전부 검색한다. 참고로, reverseRange 메서드를 사용하면 데이터는 LinkedHashMap 으로 리턴된다. 테스트 코드를 작성하여, 최근 5개의 데이터를 정상적으로 조회하는지 확인해보자. 


이미 방문했던 페이지를 한번 더 방문하게 되면 어떻게 될까?? 

Sorted Set 에서는 중복 데이터를 체크한다. 테스트 코드를 돌려서 중복 데이터를 추가해보자. 

아래와 같이 중복데이터 없이, 정렬이 잘 된것을 확인할 수 있다.

https://github.com/sieunkr/spring-data/tree/master/spring-data-redis-sortedset


Sorted Set 을 사용해서 방문기록 저장,조회 기능을 심플하게 구현하였다. 


마무리


간단한 샘플 예시로 레디스 의 List, Sorted Set 에 대해서 알아봤다. Sorted Set 은 아주 사용하기 쉽고, 레디스 에서 가장 발전된(?) 자료형이다. 하지만, 이 글은 시간복잡도를 고려하지 않고 작성한 글이다. 실무에서는 시간복잡도 및 고려해야할 사항이 많다. List, Sorted Set 어떤 자료형을 사용할지에 대해서는 각자 서비스에서 어떤 자료형이 적합한지 고민을 많이 해야 한다. 허접한 글이지만, 오늘도 이정도로 글을 마치겠다. 



추가로, 

레디스 전문가가 있으시다면, 레디스 활용 사례에 대해서 댓글로 피드백을 해주길 바랍니다. ^^ 

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari