Redis 집합
Redis 집합 (Sets)
Redis의 집합(Set) 데이터 타입에 대해 알아볼게요. 중복 없는 고유한 문자열 모음을 다룰 때, 그리고 집합 연산(교집합·합집합·차집합)이 필요할 때 아주 유용해요.
A Redis set(집합)은 고유한 문자열(멤버)의 순서 없는 컬렉션이에요. Redis 집합을 사용하면 다음과 같은 작업을 효율적으로 할 수 있어요.
- 고유한 항목 추적하기 (예: 특정 블로그 게시물에 접근하는 모든 고유 IP 주소 추적)
- 관계 표현하기 (예: 특정 역할을 가진 모든 사용자의 집합)
- 교집합, 합집합, 차집합 같은 일반적인 집합 연산 수행하기
예시 (Examples)
프랑스와 미국에서 경주하는 자전거들의 집합을 저장한다고 해볼게요. 이미 존재하는 멤버를 추가하면 무시된다는 점에 주의하세요.
멤버 추가 (SADD)
SADD로 집합에 멤버를 추가하면, 고유한 항목을 저장할 수 있어요 (중복은 조용히 무시돼요).
> 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
멤버 존재 확인 (SISMEMBER)
SISMEMBER로 집합 멤버십을 테스트하면 O(1) 조회가 가능해요 (리스트를 확인하는 것보다 훨씬 빨라요).
> SISMEMBER bikes:racing:usa bike:1
(integer) 1
> SISMEMBER bikes:racing:usa bike:2
(integer) 0
교집합 구하기 (SINTER)
두 경주에 모두 출전하는 자전거가 무엇인가요? SINTER는 여러 집합 사이의 공통 멤버를 찾아요 (예: 여러 그룹에 속한 사용자 찾기).
> SINTER bikes:racing:france bikes:racing:usa
1) "bike:1"
크기 세기 (SCARD)
프랑스 경주에 출전하는 자전거는 몇 대인가요? SCARD로 집합의 기수(cardinality)를 구할 수 있어요.
> SCARD bikes:racing:france
(integer) 3
튜토리얼 (Tutorial)
SADD 명령은 집합에 새 요소를 추가해요. 이 밖에도 요소가 이미 존재하는지 테스트하거나 여러 집합의 교집합·합집합·차집합을 수행하는 등 다양한 작업을 할 수 있어요.
모든 멤버 가져오기 (SMEMBERS)
SMEMBERS로 집합의 모든 멤버를 가져올 수 있어요 — 단, 순서가 보장되지는 않는다는 점을 기억하세요.
> 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에 모든 요소를 반환하라고 했어요. 집합에는 순서 보장이 없어요. Redis는 매 호출마다 요소를 어떤 순서로든 자유롭게 반환할 수 있어요.
일괄 멤버십 확인 (SMISMEMBER)
Redis에는 집합 멤버십을 테스트하는 명령이 있어요. 이 명령들은 단일 항목뿐 아니라 여러 항목에도 사용할 수 있어요.
# 프랑스 경주 집합을 재생성해 이 예시가 단독으로 실행되게 합니다.
> 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
차집합 구하기 (SDIFF)
두 집합의 차이를 찾을 수도 있어요. 예를 들어 프랑스에는 경주하지만 미국에는 경주하지 않는 자전거를 알고 싶다고 해볼게요.
> 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"
복잡한 관계 분석 (SINTER / SUNION / SDIFF)
올바른 Redis 명령을 사용하면 구현하기 쉬운 다른 비자명한 작업들도 있어요. 예를 들어 프랑스, 미국, 그리고 다른 몇몇 경주에 출전하는 모든 자전거 목록을 원한다고 해볼게요. 이를 위해 여러 집합 사이의 교집합을 수행하는 SINTER 명령을 사용할 수 있어요. 교집합 외에도 합집합, 차집합 등을 수행할 수 있어요. 예를 들어 세 번째 경주를 추가하면 이 명령들 중 일부를 볼 수 있어요.
> DEL bikes:racing:france bikes:racing:usa bikes:racing:italy
(integer) 2
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
> SADD bikes:racing:italy bike:1 bike:2 bike:3 bike:4
(integer) 4
> SINTER bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:1"
> SUNION bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:2"
2) "bike:1"
3) "bike:4"
4) "bike:3"
> SDIFF bikes:racing:france bikes:racing:usa bikes:racing:italy
(empty array)
> SDIFF bikes:racing:france bikes:racing:usa
1) "bike:3"
2) "bike:2"
> SDIFF bikes:racing:usa bikes:racing:france
1) "bike:4"
SDIFF 명령은 모든 집합 사이의 차이가 비어 있으면 빈 배열을 반환한다는 점을 알 수 있어요. 또 SDIFF에 전달되는 집합의 순서가 중요하다는 점도 주목하세요 — 차이는 교환 법칙이 성립하지 않기 때문이에요.
멤버 제거 (SREM / SPOP / SRANDMEMBER)
집합에서 항목을 제거하고 싶을 때는 SREM 명령으로 집합에서 하나 이상의 항목을 제거하거나, SPOP 명령으로 집합에서 무작위 항목을 제거할 수 있어요. 제거하지 않고 집합에서 무작위 항목을 반환하려면 SRANDMEMBER 명령을 사용할 수 있어요.
> DEL bikes:racing:france
(integer) 1
> SADD bikes:racing:france bike:1 bike:2 bike:3 bike:4 bike:5
(integer) 5
> SREM bikes:racing:france bike:1
(integer) 1
> SPOP bikes:racing:france
"bike:3"
> SMEMBERS bikes:racing:france
1) "bike:2"
2) "bike:4"
3) "bike:5"
> SRANDMEMBER bikes:racing:france
"bike:2"
제한 사항 (Limits)
Redis 집합의 최대 크기는 2^32 - 1 (4,294,967,295) 멤버예요.
성능 (Performance)
대부분의 집합 연산 — 항목 추가, 제거, 집합 멤버 여부 확인 등 — 은 O(1)이에요. 즉 매우 효율적이란 뜻이죠. 그러나 수십만 개 이상의 멤버를 가진 대형 집합에서는 SMEMBERS 명령을 실행할 때 주의해야 해요. 이 명령은 O(n)이고 단일 응답으로 전체 집합을 반환하기 때문이에요. 대안으로, 집합의 모든 멤버를 반복적으로 가져오는 SSCAN을 고려해 보세요.
대안 (Alternatives)
고유한 항목을 추적하고 관계를 표현하는 데 집합이 유용하지만, 순서가 필요한 경우에는 Sorted Sets나 다른 데이터 타입이 더 적합할 수 있어요.