Redis 不仅仅是一个键值缓存——它是一个数据结构服务器。每种 Redis 数据类型都针对特定用例进行了优化,选择正确的数据结构可以大幅简化应用程序逻辑。本综合指南涵盖了所有七种核心 Redis 数据结构,并提供了实际示例和性能特征。
字符串(Strings)
最基本的 Redis 类型。字符串存储任何字节序列(文本、整数、序列化的 JSON、二进制数据)。最大大小为 512 MB。
# STRINGS — the simplest type, just bytes
# Basic set and get
SET user:1000:name "Alice"
GET user:1000:name # "Alice"
# Set with expiry (TTL in seconds)
SET session:abc123 "user_data" EX 3600 # expires in 1 hour
SET session:abc123 "user_data" PX 3600000 # expires in 1 hour (milliseconds)
SET session:abc123 "user_data" EXAT 1800000000 # expires at Unix timestamp
# Atomic counter operations
SET page:home:views 0
INCR page:home:views # 1
INCR page:home:views # 2
INCRBY page:home:views 10 # 12
DECR page:home:views # 11
# String manipulation
SET greeting "Hello"
APPEND greeting " World" # "Hello World"
STRLEN greeting # 11
# GET and set in one operation
GETSET page:home:views 0 # returns old value, resets to 0
SETNX lock:resource "owner" # set only if not exists (mutex pattern)
# Multiple keys at once
MSET user:1:name "Alice" user:1:age 30 user:2:name "Bob"
MGET user:1:name user:1:age user:2:name列表(Lists)
Redis 列表是链表。对头部或尾部的操作极快(O(1)),并支持阻塞弹出操作——非常适合任务队列。
# LISTS — ordered sequences (linked list under the hood)
# Push to left (head) or right (tail)
RPUSH tasks "task1" "task2" "task3" # right push (append)
LPUSH tasks "task0" # left push (prepend)
# Pop from left or right
LPOP tasks # "task0" (removes and returns)
RPOP tasks # "task3"
# Blocking pop (for task queues — blocks until item available)
BLPOP queue:jobs 30 # blocks up to 30 seconds
# Range (0-indexed; -1 means last element)
LRANGE tasks 0 -1 # all elements
LRANGE tasks 0 2 # first 3 elements
# Length and index access
LLEN tasks # number of items
LINDEX tasks 0 # first item (no removal)
# Queue pattern (FIFO): producer RPUSH, consumer LPOP
RPUSH queue:emails "email1" "email2"
LPOP queue:emails # "email1" (FIFO order)
# Stack pattern (LIFO): producer and consumer both on same end
LPUSH stack:undo "action1"
LPUSH stack:undo "action2"
LPOP stack:undo # "action2" (LIFO order)集合(Sets)
无序的唯一字符串集合。Redis 提供集合代数操作(并集、交集、差集),对社交功能和推荐系统非常有价值。
# SETS — unordered collections of unique strings
# Add members
SADD tags:post:42 "nodejs" "javascript" "backend"
SADD tags:post:42 "nodejs" # ignored (already exists)
# Check membership, count, remove
SISMEMBER tags:post:42 "nodejs" # 1 (true)
SCARD tags:post:42 # 3
SREM tags:post:42 "backend" # removes "backend"
# Get all members
SMEMBERS tags:post:42
# Random members (useful for recommendations)
SRANDMEMBER tags:post:42 2 # 2 random members (no removal)
SPOP tags:post:42 # 1 random member (with removal)
# Set operations (great for social features)
SADD user:1:following 2 3 4
SADD user:2:following 3 5 6
# Intersection: mutual follows
SINTER user:1:following user:2:following # {3}
# Union: anyone either user follows
SUNION user:1:following user:2:following # {2,3,4,5,6}
# Difference: who user 1 follows that user 2 doesn't
SDIFF user:1:following user:2:following # {2,4}
# Store result of set operation into a new key
SINTERSTORE mutual:1:2 user:1:following user:2:following有序集合(Sorted Sets / ZSET)
类似集合,但每个成员都有关联的浮点分数。成员按分数排序。这是排行榜、速率限制和时间序列的典型 Redis 结构。
# SORTED SETS (ZSET) — unique members, each with a float score
# Add members with scores
ZADD leaderboard 1500 "alice"
ZADD leaderboard 2300 "bob"
ZADD leaderboard 1800 "carol"
ZADD leaderboard 2300 "dave" # score tie with bob
# Score operations
ZINCRBY leaderboard 200 "alice" # alice now has 1700
# Get rank (0-indexed, lowest score first)
ZRANK leaderboard "alice" # rank by ascending score
ZREVRANK leaderboard "bob" # rank by descending score (0 = highest)
# Get members by rank range
ZRANGE leaderboard 0 -1 # all, ascending
ZRANGE leaderboard 0 -1 WITHSCORES # with scores
ZREVRANGE leaderboard 0 2 WITHSCORES # top 3
# Get members by score range
ZRANGEBYSCORE leaderboard 1000 2000 WITHSCORES
ZRANGEBYSCORE leaderboard -inf +inf LIMIT 0 10 # pagination
# Count members in score range
ZCOUNT leaderboard 1500 2000 # count between scores
# Use case: rate limiting (sliding window)
ZADD rate:user:123 1708000000 "req_1"
ZREMRANGEBYSCORE rate:user:123 -inf 1707999000 # remove old entries
ZCARD rate:user:123 # current window count哈希(Hashes)
键内的键值映射。非常适合表示具有多个字段的对象。比将每个字段作为单独键存储更节省内存。
# HASHES — field-value pairs (like objects/maps)
# Set fields
HSET user:1000 name "Alice" age 30 email "alice@example.com"
# Get one field or all fields
HGET user:1000 name # "Alice"
HMGET user:1000 name age # ["Alice", "30"]
HGETALL user:1000 # {name: Alice, age: 30, email: ...}
# Field existence, count, keys, values
HEXISTS user:1000 email # 1
HLEN user:1000 # 3
HKEYS user:1000 # [name, age, email]
HVALS user:1000 # [Alice, 30, alice@example.com]
# Numeric operations on hash fields
HINCRBY user:1000 login_count 1
HINCRBYFLOAT user:1000 balance 10.50
# Delete a field
HDEL user:1000 age
# Scan a hash incrementally (for large hashes)
HSCAN user:1000 0 MATCH "em*" COUNT 10流(Streams)
Redis Streams(5.0 版本添加)是一种追加专用日志数据结构。它们支持消费者组,用于分布式、容错的事件处理——类似 Apache Kafka,但更简单。
# STREAMS — append-only log (like Kafka, but built-in)
# Add an event (auto-generated ID: timestamp-sequence)
XADD events:orders * user_id 123 product "Widget" amount 9.99
XADD events:orders * user_id 456 product "Gadget" amount 24.99
# Add with explicit ID
XADD events:orders 1708000000000-0 user_id 789 product "Doohickey" amount 4.99
# Read from beginning
XRANGE events:orders - +
XRANGE events:orders - + COUNT 10 # limit results
# Read from last N entries
XREVRANGE events:orders + - COUNT 5
# Consumer groups (for distributed processing)
XGROUP CREATE events:orders processors $ MKSTREAM
# Consumer reads from group
XREADGROUP GROUP processors worker1 COUNT 10 STREAMS events:orders >
# Acknowledge processed messages
XACK events:orders processors 1708000000000-0
# Stream info
XLEN events:orders # total messages
XINFO STREAM events:orders # metadata数据结构用例指南
| Structure | Common Use Cases | Example |
|---|---|---|
| String | Sessions, tokens, counters, caching, flags | session:abc = user data |
| List | Task queues, activity feeds, recent items | queue:jobs — RPUSH/BLPOP |
| Set | Tags, likes, follows, unique visitors, permissions | user:1:following |
| Sorted Set | Leaderboards, rate limiting, priority queues, timelines | leaderboard — score = points |
| Hash | User profiles, product catalog, config objects | user:1000 — name, age, email |
| Stream | Event log, message queue, audit trail, IoT data | events:orders — consumer groups |
时间复杂度参考
| Operation | Complexity | Note |
|---|---|---|
| GET/SET (String) | O(1) | Constant time |
| LPUSH/RPOP (List) | O(1) | Head/tail only |
| LRANGE (List) | O(n) | n = elements returned |
| SADD/SREM (Set) | O(1) | Per element |
| SINTER (Set) | O(N*M) | N = min set size, M = sets |
| ZADD (Sorted Set) | O(log N) | Skip list insertion |
| ZRANGE (Sorted Set) | O(log N + M) | M = elements returned |
| HSET/HGET (Hash) | O(1) | Per field |
| HGETALL (Hash) | O(n) | n = fields |
最佳实践
- 使用一致的键命名约定:object_type:id:field(例如 user:1000:profile)。这使键扫描和 TTL 管理变得可预测。
- 始终为会话数据、令牌和缓存条目设置 TTL。对于没有逐出策略必须永久保存的数据,Redis 不是主存储。
- 对对象使用 HSET 而非单独的字符串键。带字段的 user:1000 比 user:1000:name + user:1000:age 更高效。
- 使用管道(MULTI/EXEC 或客户端管道)批量处理多个命令并减少往返延迟。
- 在生产中避免使用 KEYS *。使用带游标模式的 SCAN 进行安全的增量键枚举。
常见问题
Redis 可以持有的最大键数是多少?
Redis 每个数据库最多可持有 2^32 - 1 个键(约 42 亿)。实际上,受可用 RAM 限制。拥有 32 GB RAM 的 Redis 实例可以轻松持有数亿个键。
何时应该对对象使用 Hash 而非 String?
在以下情况使用 Hash:(1) 需要在不获取整个对象的情况下更新单个字段,(2) 有很多字段并希望通过紧凑哈希编码节省内存,或 (3) 在对象内使用 HINCRBY 作为计数器。在以下情况使用 String(JSON 序列化):始终原子性读/写整个对象,或需要索引 JSON 内容。
Redis 持久性如何与数据结构配合工作?
所有 Redis 数据结构都被两种持久性机制同等保存:RDB(定时快照)和 AOF(追加专用文件——记录每个写命令)。内存中的数据结构无缝序列化/反序列化。使用 RDB 进行备份,AOF 用于持久性,或两者都用以获得最大安全性。
EXPIRE 和 TTL 有什么区别?
EXPIRE key seconds 设置键的过期时间。TTL key 返回剩余生存时间(秒)(-1 表示无过期,-2 表示键不存在)。PERSIST 移除过期时间。EXPIREAT 使用 Unix 时间戳设置绝对过期时间。PTTL 和 PEXPIRE 以毫秒为单位。
如何使用 Redis 有序集合进行速率限制?
使用有序集合的滑动窗口速率限制:(1) 使用当前时间戳作为分数,(2) ZADD rate:user:123 timestamp request_id,(3) 使用 ZREMRANGEBYSCORE 删除比时间窗口更早的条目,(4) 使用 ZCARD 计算剩余条目,(5) 如果计数 < 限制,允许请求。当包装在 Lua 脚本或 MULTI/EXEC 中时,这是准确和原子的。