Redis SORT

chaean·2025년 1월 14일

Redis

목록 보기
5/8

SORT

SORT 명령어는 정렬된 결과를 반환하는 데 사용됩니다.

주로 정렬 작업이 필요한 리스트(list), 셋(set), 정렬 셋(Sorted set) 데이터 타입과 함께 사용됩니다.

SORT key 
[BY pattern] : 정렬 기준
[LIMIT offset count] : offest만큼 건너뛰고 개수 지정
[GET pattern] : 정렬된 항목에 따라 다른 키의 값을 가져옴 여러 개 사용 가능
[ASC|DESC] : 정렬 
[ALPHA] : 숫자가 아닌 문자열 데이터를 정려할 때 사용
[STORE destination] : 정렬 결과를 반환하지 않고 지정된 키에 지정

정렬 셋(Sorted set)에서 정렬(SORT)를 왜 사용해??

SORT 명령어를 반드시 데이터 정렬에만 사용하지 않음.

  • 여러 데이터 소스를 결합하기 위한 범용 명령어에 가까움

Sorted Set과 SORT 명령어의 score는 조금 다름

  • SORT 명령어의 score는 Member를 의미
HSET books:good title 'Good Book' year 1950
HSET books:bad title 'Bad Book' year 1930
HSET books:ok title 'OK Book' year 1940

ZADD books:likes 999 good
ZADD books:likes 0 bad
ZADD books:likes 40 ok

SORT books:likes // Error -> good, bad, ok를 통해 정렬하려고 했기 때문
SORT books:likes ALPHA // OK. -> 알파벳 순으로 정렬하라는 ALPHA 명령어
SORT books:likes ALPHA LIMIT 0 2 // OK.
SORT books:likes BY books:*->year // OK.

'
'

[
  "bad",
  "ok",
  "good"
]
  • books:likes : 정렬 대상이 되는 Sorted Set 키입니다. 여기에는 책들의 ID(good, bad, ok)와 각 ID의 점수(좋아요 수)가 저장되어 있습니다.
  • BY books:*->year : books:*는 각 책의 해시 키를 참조. (good, abd, ok) ->year는 각 책 해시에서 year 필드 값을 가져와 정렬 기준으로 사용.
SORT books:likes BY books:*->year GET books:*->title

'
'

[
  "Bad Book",
  "OK Book",
  "Good Book"
]
  1. 위와 같이 정렬 후 member만 추출 (year은 정렬 기준으로만 사용하고 폐기)
  2. books:*→title : Member에 해당하는 title을 조회

여러 개 사용하는 예제

SORT books:likes BY books:*->year 
    GET # // 기존의 멤버 (현재는 ID)를 추가
    GET books:*->title // 들여쓰기 필수
    GET books:*->year 
    
'
'

[
  "bad",
  "Bad Book",
  "1930",
  "ok",
  "OK Book",
  "1940",
  "good",
  "Good Book",
  "1950"
]

BY nosort : 정렬을 수행하지 않을 때 사용하는 명령어

  • 정렬 연산을 하지않기 때문에 속도를 조금 더 향상시킬 수 있다.
  • 존재하지않는 key를 입력해도 정렬을 수행하지않음.
  • 정렬이 아닌 데이터 결합 용도로 SORT를 사용한다면 BY nosort를 사용해보자

여러 개 사용하는 예제 with node-redis

export const itemsByViews = async (order: 'DESC' | 'ASC' = 'DESC', offset = 0, count = 10) => {
    // items:views는 Sorted Set이라 아무렇게나 뽑아도 오름차순으로 정렬되어있음
    // DIRECTION 옵션을 통해 내림차순으로 정렬
    const results = await client.sort(
        itemsByViewsKey(),
        {
            GET: [
                '#',
                `${itemsKey('*')}->name`,
                `${itemsKey('*')}->views`
            ],
            BY: 'nosort',
            DIRECTION: order, // 내림차순 정렬
            LIMIT: { // 개수 지정
                offset, count
            }
        }
    )

    console.log(results)
};

HGETALL을 통한 정렬

export const itemsByEndingTime = async (
	order: 'DESC' | 'ASC' = 'DESC',
	offset = 0,
	count = 10
) => {
	const ids = await client.zRange(
		itemsByEndingAtKey(),
		Date.now(),
		'+inf',
		{
			BY: 'SCORE',
			LIMIT: {
				offset,
				count
			}
		}
	);

	const results = await Promise.all(ids.map(id => client.hGetAll(itemsKey(id))));

	return results.map((item, i) => deserialize(ids[i], item));
};

위와 같은 코드를 통해 여러번 요청을 보내고 데이터를 합치는 방법도 있다.

상황에 따라 어떤 방법을 사용할지 다르니 언제 어떻게 사용할지 판단하는 능력을 길러보자.

SORT vs ZRANGE + HGETALL 비교표

항목ZRange + HGETALLSORT
Redis 명령 호출1 + N (1회 zRange + N회 hGetAll)1회 (sort)
성능상대적으로 느림 (N개 데이터 개별 조회로 네트워크 부하 발생)상대적으로 빠름 (데이터 정렬과 조회가 Redis 내부에서 수행됨)
구현 난이도쉬움 (zRange와 hGetAll 사용)약간 어려움 (sort와 필드 매핑 필요)
확장성대량 데이터 조회 시 성능 저하 가능대량 데이터 조회에 적합
유연성ID를 기반으로 세부 데이터 처리 가능명시된 필드만 가져오므로 유연성이 낮음
사용 목적데이터 조회 후 개별적인 처리가 필요한 경우데이터 정렬과 특정 필드 조회가 필요한 경우
profile
백엔드 개발자

0개의 댓글