SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

RedisFundamentals

Core concepts and commands — hands-on from the start

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore Redis Team
Expert Content

Redis — Fundamentals

Data Structures and Commands

bash
# String — caching, counters, session store
SET user:123 '{"id":123,"name":"Alice"}' EX 3600   # expire in 1 hour
GET user:123
INCR page:views               # atomic counter
INCRBY cart:123:total 2599    # add amount
SET lock:resource 1 NX PX 30000   # atomic set-if-not-exists + 30s TTL —
                                    # the actual safe distributed-lock pattern;
                                    # bare SETNX with no expiry can deadlock
                                    # forever if the lock holder crashes
GETDEL temp:key               # get and delete atomically

# Hash — object fields (memory efficient vs JSON string)
HSET user:123 name "Alice" email "alice@example.com" plan "pro"
HGET user:123 name
HGETALL user:123
HMSET user:123 name "Alice" plan "enterprise"
HINCRBY user:123 login_count 1

# List — queues, activity feeds
LPUSH tasks "send-email:user123"   # push to front
RPUSH tasks "resize-image:456"     # push to back
LRANGE tasks 0 -1                  # get all
BRPOP tasks 0                      # blocking pop (worker queue)
LPOP tasks 5                       # pop 5 items (Redis 6.2+)

# Set — unique collections, tags
SADD user:123:tags "python" "devops" "kubernetes"
SMEMBERS user:123:tags
SISMEMBER user:123:tags "python"    # check membership
SINTERSTORE common:tags user:123:tags user:456:tags  # intersection

# Sorted Set — leaderboards, rate limiting
ZADD leaderboard 1500 "alice" 1200 "bob" 1800 "charlie"
ZRANGE leaderboard 0 -1 WITHSCORES REV    # top to bottom
ZRANK leaderboard "alice"                  # rank of player
ZINCRBY leaderboard 100 "alice"           # add to score
ZRANGEBYSCORE leaderboard 1000 2000       # score range

Caching Patterns

python
import redis
import json
from functools import wraps

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# Cache-aside pattern
def get_user(user_id: int) -> dict:
    cache_key = f"user:{user_id}"

    # Try cache first
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)

    # Cache miss — fetch from DB
    user = db.query(User).filter_by(id=user_id).first()
    if not user:
        return None

    # Store in cache for 1 hour
    r.setex(cache_key, 3600, json.dumps(user.to_dict()))
    return user.to_dict()

# Cache invalidation
def update_user(user_id: int, data: dict):
    db.query(User).filter_by(id=user_id).update(data)
    db.commit()
    r.delete(f"user:{user_id}")  # Invalidate cache

# Decorator for function caching
def cache(ttl=300, key_prefix=""):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_key = f"{key_prefix}:{func.__name__}:{args}:{kwargs}"
            result = r.get(cache_key)
            if result:
                return json.loads(result)
            result = func(*args, **kwargs)
            r.setex(cache_key, ttl, json.dumps(result))
            return result
        return wrapper
    return decorator

@cache(ttl=600, key_prefix="api")
def expensive_query(filters: dict) -> list:
    return db.execute_complex_query(filters)

Rate Limiting

python
def is_rate_limited(user_id: str, limit: int = 100, window: int = 60) -> bool:
    """Sliding window rate limiter using sorted sets."""
    key = f"ratelimit:{user_id}"
    now = time.time()
    window_start = now - window

    pipe = r.pipeline()
    # Remove old entries outside window
    pipe.zremrangebyscore(key, 0, window_start)
    # Count requests in window
    pipe.zcard(key)
    # Add current request
    pipe.zadd(key, {str(now): now})
    # Set expiry
    pipe.expire(key, window)
    results = pipe.execute()

    return results[1] >= limit  # True = rate limited

# Fixed-window counter using Lua script (atomic) -- not token bucket:
# there's no refill rate or burst capacity here, just a per-window count cap
RATE_LIMIT_SCRIPT = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or "0")
if current + 1 > limit then
    return 0
end
redis.call('INCR', key)
redis.call('EXPIRE', key, window)
return 1
"""

Pub/Sub

python
# Publisher
import redis
r = redis.Redis()

r.publish('user-events', json.dumps({
    'type': 'user.registered',
    'user_id': 123,
    'timestamp': time.time()
}))

# Subscriber (in separate process)
def handle_event(message):
    data = json.loads(message['data'])
    if data['type'] == 'user.registered':
        send_welcome_email(data['user_id'])

pubsub = r.pubsub()
pubsub.subscribe(**{'user-events': handle_event})
pubsub.run_in_thread(sleep_time=0.001)

Production Configuration

redis
# redis.conf — production settings
maxmemory 4gb
maxmemory-policy allkeys-lru     # Evict least recently used when full
                                  # Options: allkeys-lru, volatile-lru, allkeys-lfu

# Persistence
appendonly yes                    # AOF persistence
appendfsync everysec              # Sync to disk every second (balance of speed/safety)
save 900 1                        # RDB snapshot: after 900s if 1 key changed
save 300 10                       # After 300s if 10 keys changed

# Security
requirepass "strong-password"
rename-command FLUSHALL ""        # Disable dangerous commands
rename-command CONFIG ""          # In production

# Replication
replicaof 10.0.0.1 6379          # Make this a replica
replica-read-only yes
Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Up Next
RedisIntermediate
Real-world patterns, best practices, and deeper topics
Also Worth Exploring
← Back to all Redis modules
InstallationIntermediate