Redis 집합
Redis 집합 (Redis sets)
Redis **집합(set)**은 고유한 문자열(member)들로 이뤄진 순서 없는 컬렉션이에요. 집합은 멤버십 확인, 교집합/합집합/차집합 같은 집합 연산에 매우 유용하고, 대부분의 연산이 O(1)이라 아주 효율적이죠. 실제 사용 예제를 따라가며 익혀볼게요.
집합(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 filter나 Cuckoo filter를 고려해 보세요.
- Redis 집합은 일종의 인덱스처럼 자주 쓰여요. 데이터를 인덱싱하고 쿼리해야 한다면 JSON 데이터 타입과 Redis Search 기능을 고려해 보세요.
더 알아보기 (Learn more)
- Redis sorted sets — 점수로 정렬된 집합
- 집합 명령어 참조 (SADD, SREM, SISMEMBER, SMISMEMBER, SINTER, SUNION, SDIFF, SCARD, SMEMBERS, SSCAN 등 19개 명령)
- Bloom filter — 대용량 멤버십 검사의 메모리 절약 대안