Redis 数据结构选型:别把所有需求都塞进 String

Redis 快不代表任何建模都合理。数据结构决定命令复杂度、内存布局、并发原子性和后续扩展方式。选型时先写出核心访问模式,再判断是否需要排序、去重、范围查询、消费确认或近似统计。

String:缓存、计数和位操作

String 适合序列化对象、计数器、分布式状态标记和 Bitmap。写缓存时同时设置 TTL,避免忘记过期:

SET user:42 '{...}' EX 300
INCR article:42:view

不要把一个不断增长的大 JSON 当数据库使用。更新单个字段需要读改写整个对象,还会增加网络与复制开销。

Hash:字段级读写

用户配置、会话摘要等小对象适合 Hash:

HSET session:123 user_id 42 status active updated_at 1720000000
HGET session:123 status

Hash 的 TTL 主要作用在整个 Key 上。字段生命周期差异很大时,应拆分 Key 或使用支持字段过期的新能力前确认服务版本与兼容性。

Set 与 Sorted Set

Set 适合去重关系、共同成员和随机抽取:

SADD article:42:likers u1 u2
SISMEMBER article:42:likers u1

Sorted Set 为成员附带 Score,适合排行榜、延迟队列和时间线索引:

ZADD hot:articles 128 article:42
ZREVRANGE hot:articles 0 19 WITHSCORES

Score 是双精度浮点数,超大整数和复合排序键要注意精度。需要同分稳定排序时,可把时间或序号编码进成员或重新设计 Score。

List 与 Stream

List 适合简单队列和两端操作,但缺少消费者组、Pending 和确认机制。需要多个消费者、消息确认、重放和消费进度时,优先考虑 Stream:

XADD events:* * type article.published article_id 42
XREADGROUP GROUP notifier worker-1 COUNT 10 BLOCK 5000 STREAMS events:* >
XACK events:* notifier 1710000000000-0

Stream 仍需要长度治理,可使用近似裁剪,并监控 Pending。它不是 RabbitMQ 的完全替代,跨服务路由、复杂重试和成熟运维场景仍要评估专业消息队列。

Bitmap 与 HyperLogLog

Bitmap 适合按用户 ID 或日期记录二值状态,例如每日签到;HyperLogLog 用很小空间估算去重数量,适合 UV 趋势,但结果是近似值,不能用于计费或精确名单。

生产使用技巧

  • 禁止在大库执行 KEYS *,使用 SCAN 分批遍历。
  • Key 带业务前缀和版本,例如 wr:article:v1:42
  • 大集合分页读取,避免一次返回数十 MB。
  • 使用 Lua 或事务保证跨多个命令的原子业务规则,但脚本应短小。
  • 监控 Big Key、Hot Key、命中率、淘汰、延迟和复制积压。
  • 缓存失效要有恢复策略,Redis 不能成为唯一事实来源。

参考资料