Skip to main content

Data Structures

Redis provides rich data structures that enable powerful operations with minimal code.

Why Data Structures Matter

Choosing the right data structure is critical for:

  • Performance: O(1) vs O(N) operations
  • Code simplicity: Use built-in operations instead of application logic
  • Memory efficiency: Compact storage

Example: Leaderboard with sorted set vs maintaining sorted list in application

  • Sorted set: ZINCRBY leaderboard 50 "alice" (O(log N))
  • Application: Read all scores, sort, increment, write back (O(N log N))

String

Overview

Binary-safe string: Can store any data (text, JSON, binary, serialized objects)

Max size: 512 MB

Common Commands

# Set and get
SET key value
GET key

# Set with expiry (seconds)
SETEX key 3600 value

# Set if not exists (useful for distributed lock)
SETNX lock:resource 1 # Returns 1 if set, 0 if already exists

# Atomic increment (useful for counters)
INCR counter # Increment by 1
INCRBY counter 10 # Increment by 10
DECR counter # Decrement by 1

# Multiple operations
MSET key1 value1 key2 value2
MGET key1 key2

# Append to string
APPEND key "suffix"

# Get substring
GETRANGE key 0 4 # Characters 0-4 (5 characters)

# Set substring
SETRANGE key 5 "hello" # Overwrite starting at position 5

Use Cases

1. Caching

# Cache API response
SETEX api:user:123 3600 '{"name":"Alice","age":25}'

# Retrieve cache
GET api:user:123 # Returns JSON string

2. Counter

# Page view counter
INCR page:home:views

# Rate limiting
INCR ratelimit:user:123:2024-02-14
EXPIRE ratelimit:user:123:2024-02-14 86400
GET ratelimit:user:123:2024-02-14 # Check count

3. Distributed Lock

# Acquire lock (SETNX + SET with expiry available in Redis 2.6.12+)
SET lock:order:123 "unique_value" NX EX 10 # Set if not exists, expire in 10s

# Release lock (only if value matches)
GET lock:order:123
if value == "unique_value":
DEL lock:order:123

# Better: Use Lua script for atomic check-and-delete
EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:order:123 "unique_value"

4. Session Storage

# Store session data
HSET session:abc123 user_id 123 login_time "2024-02-14T10:00:00Z"
EXPIRE session:abc123 3600

# Check session
EXISTS session:abc123 # Returns 1 if exists

Hash

Overview

Field-value pairs: Like a map/dictionary (similar to JSON object or HashMap)

Max fields: 4 billion per hash

Common Commands

# Set field
HSET key field value
HMSET key field1 value1 field2 value2 # Multiple fields

# Get field
HGET key field
HMGET key field1 field2 # Multiple fields
HGETALL key # All fields and values

# Check existence
HEXISTS key field

# Increment field (useful for counters within hash)
HINCRBY key field 1

# Delete field
HDEL key field

# Get all fields or values
HKEYS key # All field names
HVALS key # All values

# Number of fields
HLEN key

Use Cases

1. Object Storage (User Profile)

# Store user profile
HSET user:123 name "Alice" email "alice@example.com" age 25

# Get specific field
HGET user:123 name # Returns "Alice"

# Update single field
HSET user:123 age 26

# Get all fields
HGETALL user:123
# Returns: name "Alice" email "alice@example.com" age 26

2. Shopping Cart

# Add item to cart
HSET cart:user:123 product:456 2 # 2 units of product 456

# Get cart contents
HGETALL cart:user:123

# Update quantity
HINCRBY cart:user:123 product:456 1 # Add 1 more

# Remove item
HDEL cart:user:123 product:456

3. Counters

# Page views by URL
HINCRBY page:views:/home 1

# Daily stats
HINCRBY stats:2024-02-14 page_views 1
HINCRBY stats:2024-02-14 unique_visitors 1

List

Overview

Linked list: Ordered collection of strings (duplicate values allowed)

Max elements: 4 billion per list

Operations: O(1) for head/tail operations, O(N) for indexing

Common Commands

# Push to head (left) or tail (right)
LPUSH key value1 value2 # Push to head (left)
RPUSH key value1 value2 # Push to tail (right)

# Pop from head or tail
LPOP key # Pop from head
RPOP key # Pop from tail

# Blocking pop (wait if list empty)
BLPOP key timeout # Blocking left pop
BRPOP key timeout # Blocking right pop

# Get range
LRANGE key 0 -1 # All elements (0 to -1)
LRANGE key 0 9 # First 10 elements

# Get length
LLEN key

# Trim list (keep only specified range)
LTRIM key 0 99 # Keep first 100 elements

# Get by index
LINDEX key 0 # First element

# Set by index
LSET key 0 "new_value"

Use Cases

1. Queue (FIFO)

# Producer (enqueue)
RPUSH queue:tasks "task1"
RPUSH queue:tasks "task2"

# Consumer (dequeue)
RPOP queue:tasks # Returns "task1"

# Blocking queue (wait if empty)
BRPOP queue:tasks 30 # Wait up to 30 seconds

2. Stack (LIFO)

# Push
LPUSH stack:items "item1"

# Pop
LPOP stack:items # Returns "item1"

3. Timeline (Activity Feed)

# Add activity (newest first)
LPUSH timeline:user:123 "Post: Hello world"

# Get recent 20 activities
LRANGE timeline:user:123 0 19

# Trim to keep only latest 100
LTRIM timeline:user:123 0 99

4. Message Queue

# Producer
LPUSH queue:emails '{"to":"alice@example.com","subject":"Welcome"}'

# Consumer (blocking)
BRPOP queue:emails 0 # Block indefinitely

Set

Overview

Unordered collection of unique strings: No duplicates, unordered

Max elements: 4 billion per set

Operations: O(1) for add/remove/check membership

Common Commands

# Add members
SADD key member1 member2

# Remove members
SREM key member1

# Check membership
SISMEMBER key member # Returns 1 if member exists

# Get all members
SMEMBERS key

# Get cardinality (size)
SCARD key

# Random member
SRANDMEMBER key # Return random member (don't remove)
SPOP key # Remove and return random member

# Set operations
SINTER key1 key2 # Intersection (members in both sets)
SUNION key1 key2 # Union (all members)
SDIFF key1 key2 # Difference (members in key1 but not in key2)

# Store operation result
SINTERSTORE dest key1 key2
SUNIONSTORE dest key1 key2
SDIFFSTORE dest key1 key2

Use Cases

1. Tags

# Add tags to article
SADD article:123:tags "redis" "database" "nosql"

# Get all tags
SMEMBERS article:123:tags

# Find articles with tag (inverse index)
SADD tag:redis:articles 123
SADD tag:database:articles 123

# Articles with both "redis" and "database" tags
SINTER tag:redis:articles tag:database:articles

2. Followers/Following

# User follows others
SADD user:123:following 456 789

# User's followers
SADD user:123:followers 456 789

# Check if following
SISMEMBER user:123:following 456

# Common followers (who follows both user 123 and 456)
SINTER user:123:followers user:456:followers

# Follow count
SCARD user:123:following

3. Unique Visitors

# Track unique visitors per day
SADD visitors:2024-02-14 "ip:1.2.3.4" "ip:5.6.7.8"

# Count unique visitors
SCARD visitors:2024-02-14

# Check if visited
SISMEMBER visitors:2024-02-14 "ip:1.2.3.4"

4. Recommendation Engine

# User's liked items
SADD user:123:likes item:1 item:2 item:3

# Items similar to item:1
SADD item:1:similar item:4 item:5

# Recommend items liked by users who liked item:1 (not already liked by user:123)
SUNIONSTORE temp item:1:similar
SDIFF temp user:123:likes

Sorted Set (ZSet)

Overview

Ordered set of unique members: Each member has associated score (floating-point)

Ordering: Sorted by score (ascending by default)

Max elements: 4 billion per sorted set

Operations: O(log N) for insert/delete/rank

Common Commands

# Add member with score
ZADD key score member
ZADD key 100 "alice" 95 "bob" 120 "charlie"

# Get score
ZSCORE key member

# Increment score
ZINCRBY key 10 "alice" # alice's score becomes 110

# Get range by rank (index)
ZRANGE key 0 -1 # All members (ascending by score)
ZRANGE key 0 9 WITHSCORES # Top 10 members (lowest scores)
ZREVRANGE key 0 9 WITHSCORES # Top 10 members (highest scores)

# Get range by score
ZRANGEBYSCORE key 0 100 # Members with score 0-100
ZREVRANGEBYSCORE key 100 0 # Reverse (100 to 0)

# Get rank (0-indexed)
ZRANK key member # Rank in ascending order
ZREVRANK key member # Rank in descending order

# Count members in score range
ZCOUNT key 0 100

# Remove member
ZREM key member

# Get by rank (remove and return)
ZPOPMAX key # Remove and return member with highest score
ZPOPMIN key # Remove and return member with lowest score

Use Cases

1. Leaderboard

# Add scores
ZADD leaderboard 1000 "alice" 950 "bob" 1200 "charlie"

# Get top 10
ZREVRANGE leaderboard 0 9 WITHSCORES

# Get user rank
ZREVRANK leaderboard "alice" # Returns 2 (0-indexed)

# Increment score
ZINCRBY leaderboard 50 "alice" # Alice's score: 1000 → 1050

# Get user's score
ZSCORE leaderboard "alice" # Returns 1050

2. Priority Queue

# Add tasks with priority (score = priority, higher = more important)
ZADD queue:tasks 10 "task1" 5 "task2" 8 "task3"

# Get highest priority task
ZPOPMAX queue:tasks # Returns "task1" (priority 10)

# Get top N tasks
ZREVRANGE queue:tasks 0 4 WITHSCORES # Top 5 tasks

3. Rate Limiting (Sliding Window)

# Add request with timestamp as score
ZADD ratelimit:user:123 1707880000 "req1" 1707880001 "req2"

# Remove old requests (older than 60 seconds)
ZREMRANGEBYSCORE ratelimit:user:123 0 (1707880000 - 60000)

# Count requests in last 60 seconds
ZCARD ratelimit:user:123

# If count > 100, rate limit exceeded

4. Time Series

# Add data point (timestamp as score)
ZADD temperature:sensor:1 1707880000 "25.3" 1707880060 "25.5"

# Get last 10 readings
ZREVRANGE temperature:sensor:1 0 9 WITHSCORES

# Get data in time range
ZRANGEBYSCORE temperature:sensor:1 1707880000 1707883600

5. Ranking by Multiple Criteria

# Composite score: e.g., (rating * 10) + (review_count / 100)
ZADD products:ranked 95.23 "product:1" 87.45 "product:2"

# Search by score range
ZRANGEBYSCORE products:ranked 90 100 WITHSCORES

Redis Streams (5.0+)

Redis Streams 是类似 Kafka 的日志数据结构,支持消费者组模型:

# 添加消息
XADD mystream * sensor-id 1234 temperature 23.5

# 读取消息(消费者组)
XREADGROUP GROUP mygroup consumer1 COUNT 1 BLOCK 2000 STREAMS mystream >

# 查看流信息
XINFO STREAM mystream

核心概念

  • Entry ID: 自动生成的时间戳 ID(1640995200000-0
  • Consumer Group: 多个消费者协同处理同一流
  • Pending Entries List (PEL): 已发送但未确认的消息
  • Maxlen/XTRIM: 控制流大小,自动清理旧消息

适用场景: 事件溯源、消息队列、实时数据管道、活动跟踪

Advanced Data Types

Bitmap

SETBIT user:login:20250401 123 1    # 用户123在2025-04-01登录
GETBIT user:login:20250401 123 # 检查是否登录
BITCOUNT user:login:20250401 # 当天登录总人数

适用场景: 用户签到、在线状态、布隆过滤器底层

HyperLogLog

PFADD unique:visitors:20250401 user1 user2 user3
PFCOUNT unique:visitors:20250401 # 近似唯一访客数
PFMERGE total:month unique:day1 unique:day2 # 合并

适用场景: UV统计、基数估计(0.81%标准误差,仅12KB内存)

GEO

GEOADD locations 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
GEORADIUS locations 15 37 200 km ASC COUNT 10
GEOSEARCH locations FROMLONLAT 15 37 BYRADIUS 200 km ASC

适用场景: 附近的人、门店搜索、地理围栏

Redis 7.x/8.x 新特性

Redis Functions(替代 Lua 脚本)

# 注册函数
FUNCTION LOAD "#!lua name=mylib \n redis.register_function('my_func', function(keys, args) return args[1] end)"

# 调用函数
FCALL my_func 0 hello

Redis 8.0 原生支持向量相似性搜索(通过 RediSearch 模块集成):

# 创建向量索引
FT.CREATE my_index ON HASH PREFIX 1 doc: SCHEMA content VECTOR HNSW 6 TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE

# 添加向量文档
HSET doc:1 content <vector_bytes> title "Example Document"

# 向量搜索
FT.SEARCH my_index "*=>[KNN 5 @content $query_vec]" PARAMS 2 query_vec <vector_bytes> DIALECT 2

适用场景: AI/RAG 应用中的语义搜索、推荐系统

其他 Redis 7.x 改进

  • Client-side caching: 客户端缓存协议,减少服务端压力
  • ACL v2: 更细粒度的权限控制(按命令、key pattern)
  • Sharded Pub/Sub: 集群模式的发布订阅
  • Multi-part AOF: AOF 持久化性能优化
  • listpack 替代 ziplist: 更高效的内存编码

Data Structure Comparison

StructureOrderedUniqueOperationsUse Case
StringN/AN/AO(1)Cache, counter, lock
HashNoFields uniqueO(1)Object, profile
ListYesNoO(1) head/tailQueue, stack, timeline
SetNoMembers uniqueO(1)Tags, followers, unique
ZSetYes (by score)Members uniqueO(log N)Leaderboard, ranking

Interview Questions

Q1: How do you implement a leaderboard in Redis?

Answer: Use sorted set (ZSet). Add scores with ZADD, get top N with ZREVRANGE, get rank with ZREVRANK, increment with ZINCRBY.

Q2: What's the difference between List and Set?

Answer: List is ordered and allows duplicates, Set is unordered and enforces uniqueness. Use List for queues/timelines, Set for tags/followers.

Q3: How do you store user profiles in Redis?

Answer: Use Hash. Each field is a profile attribute (name, email, age). Allows partial updates with HSET and retrieval of specific fields with HGET.

Q4: How do you implement rate limiting?

Answer: Use counter with INCR and EXPIRE (fixed window) or sorted set with timestamps (sliding window). Check count, allow/deny based on limit.

Q5: What's the time complexity of ZADD?

Answer: O(log N) where N is number of elements in sorted set. Uses skip list internally.

Further Reading