redis技术分享——(1)




2018-06-08

blog_main_img

很多人刚开始使用 Redis 时,只把它当作简单的 `key-value` 缓存。实际上,Redis 真正强大的地方在于它提供了多种数据结构。不同的数据结构适合不同的业务场景,选对结构往往比单纯记住命令更重要。

Redis 是一个高性能的内存数据库,常被用作缓存、计数器、排行榜、消息队列、分布式锁和会话存储。

很多人刚开始使用 Redis 时,只把它当作简单的 key-value 缓存。实际上,Redis 真正强大的地方在于它提供了多种数据结构。不同的数据结构适合不同的业务场景,选对结构往往比单纯记住命令更重要。

本文主要围绕 Redis 中几种常用数据结构展开:

  • String
  • Hash
  • List
  • Set
  • Sorted Set
  • Bitmap
  • HyperLogLog
  • Stream

一、String:最基础的数据结构

String 是 Redis 中最基础、最常用的数据结构。它可以存储字符串、数字、JSON 字符串、序列化后的对象等。

常用命令

SET user:1:name "Tom"
GET user:1:name

SET count 1
INCR count
DECR count

SETEX token:abc 3600 "user-1"
TTL token:abc

常见场景

1. 缓存对象

可以把一个对象序列化成 JSON 后存入 Redis:

SET user:1 '{"id":1,"name":"Tom","age":20}'
GET user:1

这种方式简单直接,适合读取整个对象的场景。

2. 计数器

Redis 的 INCRDECR 是原子操作,非常适合做计数器:

INCR article:1001:views
INCR video:2001:likes

常见用法包括:

  • 文章阅读量
  • 视频点赞数
  • 接口调用次数
  • 短信发送次数

3. 临时 Token

登录 token、验证码、短信码等通常都有过期时间,可以使用 SETEX

SETEX login:token:abc123 7200 "user-1"
SETEX sms:code:13800000000 300 "9527"

这样 Redis 会自动删除过期数据,不需要业务代码额外清理。

二、Hash:适合存储对象字段

Hash 可以理解为一个 key 对应多个 field-value。它很适合存储对象,尤其是对象字段需要单独读写的场景。

常用命令

HSET user:1 name "Tom" age 20 city "Shanghai"
HGET user:1 name
HMGET user:1 name age
HGETALL user:1
HINCRBY user:1 age 1
HDEL user:1 city

常见场景

1. 用户信息

HSET user:1 id 1 name "Tom" age 20 level 3
HGET user:1 name
HINCRBY user:1 level 1

如果只需要修改用户的某一个字段,Hash 比整个 JSON 字符串更方便。

2. 商品库存信息

HSET product:1001 stock 500 price 99 sales 0
HINCRBY product:1001 stock -1
HINCRBY product:1001 sales 1

库存、销量、价格等字段可以独立更新。

String 和 Hash 怎么选

如果业务经常一次性读取整个对象,可以使用 String 存 JSON。

如果业务经常修改或读取对象中的某几个字段,Hash 更合适。

例如:

  • 用户详情页一次性展示完整用户信息:String 可以
  • 用户积分、等级、状态频繁独立更新:Hash 更合适

三、List:有序列表

List 是一个按照插入顺序排列的列表,可以从两端插入或弹出元素。

常用命令

LPUSH queue:email "job-1"
LPUSH queue:email "job-2"
RPOP queue:email

RPUSH messages "msg-1"
RPUSH messages "msg-2"
LRANGE messages 0 -1

常见场景

1. 简单消息队列

生产者写入任务:

LPUSH queue:order "order-1001"
LPUSH queue:order "order-1002"

消费者取出任务:

RPOP queue:order

也可以使用阻塞式命令:

BRPOP queue:order 10

BRPOP 在队列为空时会等待,适合消费者进程轮询任务。

2. 最新消息列表

LPUSH article:comments:1001 "comment-1"
LPUSH article:comments:1001 "comment-2"
LRANGE article:comments:1001 0 9

这种方式适合取最新评论、最新动态等。

注意点

List 适合简单队列,但如果业务要求更强的消息确认、失败重试、消费组等能力,应该优先考虑 Redis Stream,或者使用专业消息队列。

四、Set:无序且不重复的集合

Set 用来存储不重复的元素,适合去重、标签、关注关系、集合运算等场景。

常用命令

SADD user:1:tags "python" "redis" "mysql"
SMEMBERS user:1:tags
SISMEMBER user:1:tags "redis"
SREM user:1:tags "mysql"
SCARD user:1:tags

常见场景

1. 标签系统

SADD article:1001:tags "redis" "database" "cache"
SADD article:1002:tags "redis" "python"

Set 天然去重,适合保存标签。

2. 用户关注关系

SADD user:1:following 2 3 4
SADD user:2:followers 1

判断是否关注:

SISMEMBER user:1:following 2

3. 共同关注

SINTER user:1:following user:2:following

集合运算是 Set 的重要能力:

  • SINTER:交集
  • SUNION:并集
  • SDIFF:差集

例如社交系统中的共同好友、共同关注、兴趣匹配,都可以用 Set 来实现。

五、Sorted Set:带分数的有序集合

Sorted Set,也叫 ZSet。它和 Set 一样元素不重复,但每个元素都会关联一个分数 score,Redis 会根据分数排序。

常用命令

ZADD rank:game 100 "Tom"
ZADD rank:game 200 "Jerry"
ZADD rank:game 150 "Alice"

ZRANGE rank:game 0 -1 WITHSCORES
ZREVRANGE rank:game 0 9 WITHSCORES
ZINCRBY rank:game 50 "Tom"
ZRANK rank:game "Tom"
ZREVRANK rank:game "Tom"

常见场景

1. 排行榜

ZADD rank:article 100 "article-1"
ZADD rank:article 300 "article-2"
ZADD rank:article 200 "article-3"

获取前 10 名:

ZREVRANGE rank:article 0 9 WITHSCORES

增加文章热度:

ZINCRBY rank:article 1 "article-1"

2. 延迟队列

可以把时间戳作为 score:

ZADD delay:queue 1730000000 "task-1"
ZADD delay:queue 1730000060 "task-2"

消费者查询当前时间之前到期的任务:

ZRANGEBYSCORE delay:queue 0 1730000000

这种方式适合实现轻量级延迟任务。

3. 按时间排序的动态流

把发布时间作为 score:

ZADD user:1:feed 1730000000 "post-1001"
ZADD user:1:feed 1730000100 "post-1002"

按时间倒序取最新内容:

ZREVRANGE user:1:feed 0 19

六、Bitmap:适合大量布尔状态

Bitmap 本质上是基于 String 的位操作。它适合存储大量只有两种状态的数据,例如是否签到、是否在线、是否活跃。

常用命令

SETBIT sign:2026-04 0 1
GETBIT sign:2026-04 0
BITCOUNT sign:2026-04

常见场景

1. 用户签到

假设一个月最多 31 天,可以用每一位表示某一天是否签到:

SETBIT user:1:sign:2026-04 0 1
SETBIT user:1:sign:2026-04 1 1
SETBIT user:1:sign:2026-04 2 0

统计本月签到天数:

BITCOUNT user:1:sign:2026-04

2. 用户活跃状态

如果用用户 ID 作为偏移量,可以记录某天哪些用户活跃:

SETBIT active:2026-04-24 10001 1
GETBIT active:2026-04-24 10001

统计当天活跃用户数:

BITCOUNT active:2026-04-24

Bitmap 的优势是非常节省空间。对于大量布尔值场景,比用 Set 存用户 ID 更省内存。

七、HyperLogLog:基数统计

HyperLogLog 用于统计不重复元素数量,也就是基数统计。它的特点是占用空间极小,但结果有一定误差。

常用命令

PFADD uv:article:1001 user-1 user-2 user-3
PFADD uv:article:1001 user-2
PFCOUNT uv:article:1001

常见场景

1. UV 统计

PFADD uv:site:2026-04-24 user-1
PFADD uv:site:2026-04-24 user-2
PFADD uv:site:2026-04-24 user-1
PFCOUNT uv:site:2026-04-24

即使同一个用户访问多次,也只会统计一次。

注意点

HyperLogLog 适合只关心数量、不关心具体成员的场景。

如果你需要知道“哪些用户访问过”,应该使用 Set。

如果你只需要知道“大概有多少独立用户访问过”,HyperLogLog 更节省空间。

八、Stream:Redis 的消息流

Stream 是 Redis 5.0 引入的数据结构,适合实现消息队列、事件流、日志流等。

相比 List,Stream 支持消息 ID、消费组、消费确认和消息追踪。

常用命令

XADD order:stream * orderId 1001 status created
XADD order:stream * orderId 1002 status paid

XRANGE order:stream - +
XREAD COUNT 2 STREAMS order:stream 0

消费组示例

创建消费组:

XGROUP CREATE order:stream group-1 0 MKSTREAM

消费者读取消息:

XREADGROUP GROUP group-1 consumer-1 COUNT 1 STREAMS order:stream >

确认消息:

XACK order:stream group-1 1730000000000-0

常见场景

1. 订单事件流

XADD order:events * orderId 1001 event created
XADD order:events * orderId 1001 event paid
XADD order:events * orderId 1001 event shipped

不同消费者可以处理不同逻辑,例如:

  • 发送通知
  • 更新搜索索引
  • 记录审计日志
  • 触发积分发放

2. 轻量级消息队列

Stream 比 List 更适合消息队列,因为它支持:

  • 消费组
  • 消息确认
  • 未确认消息查询
  • 多消费者协作

如果项目已经使用 Redis,且消息量不算特别大,Stream 是一个实用选择。

九、不同数据结构怎么选

下面是一个简单对照表:

数据结构 适合场景
String 缓存值、计数器、验证码、Token
Hash 用户对象、商品对象、字段独立更新
List 简单队列、最新列表、评论列表
Set 去重、标签、关注关系、集合运算
Sorted Set 排行榜、延迟队列、按分数排序
Bitmap 签到、活跃状态、布尔标记
HyperLogLog UV、去重数量统计
Stream 消息队列、事件流、消费组

十、Redis Key 设计建议

Redis 的 key 命名建议保持清晰和统一:

业务名:对象名:对象ID:字段

例如:

user:1
user:1:profile
user:1:following
article:1001:views
rank:article:daily
uv:site:2026-04-24

几个建议:

  • 使用冒号 : 分隔层级
  • key 不要太长,但要能表达含义
  • 临时数据设置过期时间
  • 大 key 要拆分,避免一次读取过多数据
  • 不要在生产环境使用 KEYS * 扫描全量 key,应该使用 SCAN

十一、总结

Redis 不是只能做缓存。它的多种数据结构可以覆盖很多常见业务需求。

简单来说:

  • 存简单值,用 String
  • 存对象字段,用 Hash
  • 存队列或列表,用 List
  • 存不重复集合,用 Set
  • 做排行榜,用 Sorted Set
  • 存大量布尔状态,用 Bitmap
  • 统计 UV,用 HyperLogLog
  • 做消息流,用 Stream

真正用好 Redis 的关键,不是记住所有命令,而是根据业务场景选择合适的数据结构。数据结构选对了,代码会更简单,性能也会更稳定。