Redis 집합

Redis 집합 (Redis sets)

Redis **집합(set)**은 고유한 문자열(member)들로 이뤄진 순서 없는 컬렉션이에요. 집합은 멤버십 확인, 교집합/합집합/차집합 같은 집합 연산에 매우 유용하고, 대부분의 연산이 O(1)이라 아주 효율적이죠. 실제 사용 예제를 따라가며 익혀볼게요.

출처: Redis 공식 문서 — Redis sets

집합(set)이란?

Redis 집합은 고유한 문자열(member)들로 이뤄진 순서 없는 컬렉션이에요. 집합을 사용해 다음과 같은 일을 효율적으로 할 수 있어요.

  • 고유 아이템 추적 — 예: 특정 블로그 글에 접근한 모든 고유 IP 주소 추적
  • 관계 표현 — 예: 특정 역할을 가진 모든 사용자 집합
  • 일반 집합 연산 — 교집합(intersection), 합집합(union), 차집합(difference)

집합은 이벤트 목록(클릭 스트림), 사용자 목록(그룹 대화), 제품 목록(추천 목록), 참여도(likes, shares) 등을 관리하는 데 좋아요.

기본 예제 (Examples)

프랑스와 미국에서 경주 중인 자전거 집합을 저장해 볼게요. 이미 존재하는 멤버를 추가하면 조용히 무시된다는 점을 기억하세요.

> SADD bikes:racing:france bike:1
(integer) 1
> SADD bikes:racing:france bike:1
(integer) 0
> SADD bikes:racing:france bike:2 bike:3
(integer) 2
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2

bike:1이나 bike:2가 미국 경주에 있는지 확인해 볼게요. SISMEMBER는 O(1) 조회라 리스트를 확인하는 것보다 훨씬 빨라요.

> SISMEMBER bikes:racing:usa bike:1
(integer) 1
> SISMEMBER bikes:racing:usa bike:2
(integer) 0

두 경주에 모두 출전하는 자전거는 어떤 것일까요? SINTER로 교집합을 구해요.

> SINTER bikes:racing:france bikes:racing:usa
1) "bike:1"

프랑스에서 경주 중인 자전거는 몇 대일까요? SCARD로 집합의 크기(cardinality)를 얻어요.

> SCARD bikes:racing:france
(integer) 3

튜토리얼 (Tutorial)

SADD 명령은 집합에 새 요소를 추가해요. 그 외에도 요소 존재 여부 확인, 여러 집합 간 교집합/합집합/차집합 연산 등 다양한 연산을 할 수 있어요.

> DEL bikes:racing:france
(integer) 1
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SMEMBERS bikes:racing:france
1) "bike:3"
2) "bike:1"
3) "bike:2"

집합에는 순서 보장이 없어요. Redis는 호출할 때마다 요소를 어떤 순서로든 반환할 수 있습니다.

배치 멤버십 확인도 가능해요. SMISMEMBER로 여러 아이템을 한 번에 검사해 서버 왕복을 줄일 수 있어요.

> DEL bikes:racing:france
(integer) 1
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SISMEMBER bikes:racing:france bike:1
(integer) 1
> SMISMEMBER bikes:racing:france bike:2 bike:3 bike:4
1) (integer) 1
2) (integer) 1
3) (integer) 0

두 집합의 차집합도 구할 수 있어요. 프랑스에는 경주하지만 미국에는 없는 자전거를 찾아볼게요.

> DEL bikes:racing:usa
(integer) 1
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
> SDIFF bikes:racing:france bikes:racing:usa
1) "bike:3"
2) "bike:2"

참고: SDIFF는 인자 순서가 중요해요. SDIFF A B는 A에는 있지만 B에는 없는 요소를 반환해요.

여러 경주(프랑스, 미국 등)에 모두 출전하는 자전거 목록은 SINTER로 구할 수 있어요.

> SINTER bikes:racing:france bikes:racing:usa
1) "bike:1"

합집합(union)은 SUNION으로 구해요.

> SUNION bikes:racing:france bikes:racing:usa
1) "bike:1"
2) "bike:4"
3) "bike:3"
4) "bike:2"

SREM으로 요소를 제거하고, SPOP으로 임의 요소를 꺼내며, SRANDMEMBER로 임의 요소(제거 없이)를 얻을 수 있어요.

> SREM bikes:racing:france bike:5
(integer) 1
> SPOP bikes:racing:france
"bike:2"
> SRANDMEMBER bikes:racing:france
"bike:2"

한계 (Limits)

Redis 집합의 최대 크기는 2^32 - 1 (4,294,967,295)개 멤버예요.

성능 (Performance)

추가, 제거, 멤버십 확인을 포함한 대부분의 집합 연산은 O(1) 이에요. 매우 효율적이라는 뜻이죠.

다만 멤버가 수십만 개 이상인 큰 집합에서는 SMEMBERS 명령에 주의해야 해요. 이 명령은 **O(n)**이며 전체 집합을 단일 응답으로 반환해요. 대안으로 SSCAN을 쓰면 집합의 모든 멤버를 반복적으로(iteratively) 조회할 수 있어요.

대안 (Alternatives)

  • 큰 데이터셋(또는 스트리밍 데이터)에서 집합 멤버십 확인은 메모리를 많이 쓸 수 있어요. 메모리 사용이 걱정되고 완벽한 정밀도가 필요 없다면, 집합 대신 Bloom filterCuckoo filter를 고려해 보세요.
  • Redis 집합은 일종의 인덱스처럼 자주 쓰여요. 데이터를 인덱싱하고 쿼리해야 한다면 JSON 데이터 타입Redis Search 기능을 고려해 보세요.

더 알아보기 (Learn more)