Redis Sets

Sets are unordered collections of unique strings. They provide O(1) membership testing and support powerful set operations like union, intersection, and difference.

When Redis Sets Are the Right Choice

Choose sets when uniqueness and fast membership checks matter more than order. They are a natural fit for tags, participant pools, feature flags, friend lists, and unique-visitor tracking.

Key Characteristics

• Unique members only (automatic deduplication) • O(1) add, remove, and membership test • Set operations: union, intersection, difference • Random member selection supported

Basic Operations

# Add members
SADD tags:post:1 "redis" "database" "nosql"
SADD tags:post:1 "redis"                # Returns 0 (already exists)

# Check membership
SISMEMBER tags:post:1 "redis"           # → 1 (true)
SISMEMBER tags:post:1 "mysql"           # → 0 (false)
SMISMEMBER tags:post:1 "redis" "mysql"  # Check multiple → [1, 0]

# Get all members
SMEMBERS tags:post:1

# Count members
SCARD tags:post:1                       # → 3

Remove Members

SREM tags:post:1 "nosql"                # Remove one member
SREM tags:post:1 "tag1" "tag2" "tag3"   # Remove multiple

# Pop random member(s)
SPOP tags:post:1                        # Remove and return 1 random
SPOP tags:post:1 3                      # Remove and return 3 random

Set Operations

Powerful operations for combining and comparing sets.

SADD tags:post:1 "redis" "database" "nosql"
SADD tags:post:2 "redis" "caching" "performance"

# Intersection (common elements)
SINTER tags:post:1 tags:post:2          # → ["redis"]

# Union (all unique elements)
SUNION tags:post:1 tags:post:2          # → all unique tags

# Difference (in first but not in second)
SDIFF tags:post:1 tags:post:2           # → ["database", "nosql"]
SDIFF tags:post:2 tags:post:1           # → ["caching", "performance"]

Store Set Operation Results

# Store intersection in new key
SINTERSTORE common:tags tags:post:1 tags:post:2

# Store union in new key
SUNIONSTORE all:tags tags:post:1 tags:post:2

# Store difference in new key
SDIFFSTORE unique:post1 tags:post:1 tags:post:2

Random Selection

# Get random member(s) without removing
SRANDMEMBER tags:post:1                 # 1 random member
SRANDMEMBER tags:post:1 3               # 3 random members
SRANDMEMBER tags:post:1 -3              # 3 random (may repeat)

# Pop random member(s) (removes them)
SPOP tags:post:1                        # Pop 1
SPOP tags:post:1 3                      # Pop 3

Move Between Sets

# Atomically move member between sets
SMOVE source_set dest_set "member"

# Example: Move user from pending to approved
SMOVE users:pending users:approved "user:123"

Scanning Large Sets

# Non-blocking iteration
SSCAN tags:post:1 0 COUNT 100           # Start from cursor 0
SSCAN tags:post:1 17 COUNT 100          # Continue from cursor
SSCAN tags:post:1 0 MATCH redis* COUNT 100  # With pattern filter

Use Case: Social Graph

Track followers/following relationships.

# Following relationships
SADD following:alice "bob" "charlie" "dave"
SADD following:bob "alice" "eve"
SADD followers:bob "alice" "charlie"

# Find mutual follows (who both Alice and Bob follow)
SINTER following:alice following:bob

# Find all people in extended network
SUNION following:alice following:bob

# Check if Alice follows Bob
SISMEMBER following:alice "bob"

# Get follow counts
SCARD following:alice                   # Following count
SCARD followers:alice                   # Followers count

Use Case: Unique Visitors

# Track daily unique visitors
today = "2024-01-15"

def track_visitor(visitor_id):
    key = f"visitors:{today}"
    SADD(key, visitor_id)
    EXPIRE(key, 86400 * 7)  # Keep for 7 days

def get_unique_count(date):
    return SCARD(f"visitors:{date}")

def get_weekly_uniques():
    keys = [f"visitors:{day}" for day in last_7_days()]
    temp_key = "visitors:week:temp"
    SUNIONSTORE(temp_key, *keys)
    count = SCARD(temp_key)
    DEL(temp_key)
    return count

Use Case: Tagging System

# Tag posts
SADD post:123:tags "redis" "tutorial" "database"
SADD post:456:tags "redis" "performance" "caching"

# Reverse index: posts by tag
SADD tag:redis:posts "123" "456"
SADD tag:tutorial:posts "123"

# Find posts with multiple tags
SINTER tag:redis:posts tag:tutorial:posts

# Find all posts with any of these tags
SUNION tag:redis:posts tag:caching:posts

Use Case: Random Giveaway

# Add participants
SADD giveaway:123 "user:1" "user:2" "user:3" "user:4" "user:5"

# Pick winner (removes from set)
winner = SPOP giveaway:123
print(f"Winner: {winner}")

# Pick 3 winners
winners = SPOP giveaway:123 3

# Preview random participants without removing
preview = SRANDMEMBER giveaway:123 5

Common Redis Set Mistakes

A common mistake is expecting stable order from a set. Redis sets are unordered, so if display order, ranking, or recency matters, a sorted set or list is usually the correct type instead.

Related

Knowledge is power.