DevToolBox免费
博客

Redis 数据结构指南:字符串、哈希、列表、集合与有序集合

14 分钟作者 DevToolBox

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

数据结构用例指南

StructureCommon Use CasesExample
StringSessions, tokens, counters, caching, flagssession:abc = user data
ListTask queues, activity feeds, recent itemsqueue:jobs — RPUSH/BLPOP
SetTags, likes, follows, unique visitors, permissionsuser:1:following
Sorted SetLeaderboards, rate limiting, priority queues, timelinesleaderboard — score = points
HashUser profiles, product catalog, config objectsuser:1000 — name, age, email
StreamEvent log, message queue, audit trail, IoT dataevents:orders — consumer groups

时间复杂度参考

OperationComplexityNote
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

最佳实践

  1. 使用一致的键命名约定:object_type:id:field(例如 user:1000:profile)。这使键扫描和 TTL 管理变得可预测。
  2. 始终为会话数据、令牌和缓存条目设置 TTL。对于没有逐出策略必须永久保存的数据,Redis 不是主存储。
  3. 对对象使用 HSET 而非单独的字符串键。带字段的 user:1000 比 user:1000:name + user:1000:age 更高效。
  4. 使用管道(MULTI/EXEC 或客户端管道)批量处理多个命令并减少往返延迟。
  5. 在生产中避免使用 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 中时,这是准确和原子的。

相关工具

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON Formatter#Hash GeneratorB→Base64 Encode Online

相关文章

PostgreSQL JSONB 指南:查询、索引与全文搜索

2026年PostgreSQL JSONB完整指南:嵌套JSON查询、GIN索引性能优化、全文搜索集成与MongoDB迁移。

API 限流指南:策略、算法与实现

API 限流完整指南。学习令牌桶、滑动窗口、漏桶算法及代码示例。包含 Express.js 中间件、Redis 分布式限流和最佳实践。