Skip to content

第三章 五大基础数据类型

Redis 是 键值型 数据库:每个键(key)对应一个值(value)。值的「形态」由 数据类型 决定。掌握五种基础类型,就等于掌握了 Redis 日常开发的半壁江山。

本章用一个贯穿场景 「星选商城」(电商 + 轻社交)来串联讲解:用户、商品、订单、动态、标签、排行榜都落在 Redis 里,你可以边读边在 redis-cli 里敲命令。

                    ┌─────────────┐
         key        │   value     │  ← 类型决定 value 怎么存、怎么用
    "user:1001" ──► │  (String)   │
    "cart:1001" ──► │  (Hash)     │
    "feed:home" ──► │  (List)     │
    "tag:数码"    ──► │  (Set)      │
    "hot:sales"   ──► │  (ZSet)     │
                    └─────────────┘

阅读约定:下文示例默认你已连接本机 Redis(redis-cli),且未设置密码。若 key 已存在,可先 DEL key 再重做演示。


3.1 String(字符串)

是什么

String 是最简单的类型:一个 key 对应一段二进制安全字符串(最大约 512MB)。可以存文本、数字字符串、序列化后的 JSON、甚至图片二进制(一般不推荐大对象)。

  key              value (String)
  ─────────────────────────────
  product:2001     "{\"name\":\"耳机\"}"
  views:2001       "12840"

常用命令(redis-cli 示例)

命令作用
SET / GET设置、读取单个字符串
MSET / MGET一次设置、读取多个 key
INCR / DECR将数字字符串原子自增、自减
EXPIRE给已有 key 设置过期秒数
SET key value EX seconds写入同时设过期(常用)
SETNX仅当 key 不存在时才设置(常用于锁)

基础读写

text
127.0.0.1:6379> SET product:2001:detail "{\"id\":2001,\"name\":\"降噪耳机\",\"price\":899}"
OK
127.0.0.1:6379> GET product:2001:detail
"{\"id\":2001,\"name\":\"降噪耳机\",\"price\":899}"

127.0.0.1:6379> MSET cache:page:home "<html>...</html>" cache:page:about "<html>...</html>"
OK
127.0.0.1:6379> MGET cache:page:home cache:page:about
1) "<html>...</html>"
2) "<html>...</html>"

计数器(原子操作,并发安全)

text
127.0.0.1:6379> SET views:2001 100
OK
127.0.0.1:6379> INCR views:2001
(integer) 101
127.0.0.1:6379> INCRBY views:2001 9
(integer) 110
127.0.0.1:6379> DECR views:2001
(integer) 109

过期与缓存

text
127.0.0.1:6379> SET session:user:1001 "token-abc" EX 3600
OK
127.0.0.1:6379> TTL session:user:1001
(integer) 3598

127.0.0.1:6379> SET promo:banner "618大促" EX 86400
OK
127.0.0.1:6379> EXPIRE promo:banner 7200
(integer) 1
127.0.0.1:6379> TTL promo:banner
(integer) 7199

SETNX 与简单分布式锁思路

text
127.0.0.1:6379> DEL lock:pay:order:9001
(integer) 1
127.0.0.1:6379> SETNX lock:pay:order:9001 "instance-A"
(integer) 1
127.0.0.1:6379> SETNX lock:pay:order:9001 "instance-B"
(integer) 0

第二个客户端拿不到锁(返回 0)。生产环境需配合 唯一值 + Lua/原子释放 + 过期时间(如 SET key value NX EX),这里仅演示概念。

典型场景(星选商城)

  • 缓存:商品详情 JSON、首页 HTML 片段,配合 EX 控制失效时间。
  • 计数器:商品浏览量 views:商品ID、接口限流计数等,用 INCR/INCRBY
  • 分布式锁:订单支付、库存扣减等互斥操作,用 SETNXSET ... NX EX 作为锁的占位(需完善释放与续期逻辑)。

3.2 Hash(哈希)

是什么

Hash 是一个 字段(field)→ 值(value) 的映射表,适合表示一个「对象」,且可以只读写部分字段,不必像 String 那样整段 JSON 来回传。

  key: user:1001
  ┌────────────┬──────────────┐
  │ field      │ value        │
  ├────────────┼──────────────┤
  │ name       │ 小林         │
  │ level      │ 5            │
  │ city       │ 上海         │
  └────────────┴──────────────┘

常用命令(redis-cli 示例)

命令作用
HSET / HGET设置、获取单个字段
HMSET一次设置多个字段(Redis 4.0+ 也可用多条 HSET
HGETALL / HKEYS / HVALS取全部或键、值列表
HDEL删除字段
HEXISTS判断字段是否存在
HLEN字段个数

存用户资料(对象)

text
127.0.0.1:6379> HSET user:1001 name "小林" level 5 city "上海"
(integer) 3
127.0.0.1:6379> HGET user:1001 name
"小林"
127.0.0.1:6379> HGET user:1001 level
"5"

127.0.0.1:6379> HMSET product:2001 name "降噪耳机" price "899" stock "120"
OK
127.0.0.1:6379> HGETALL product:2001
1) "name"
2) "降噪耳机"
3) "price"
4) "899"
5) "stock"
6) "120"

删除与存在性

text
127.0.0.1:6379> HEXISTS user:1001 city
(integer) 1
127.0.0.1:6379> HDEL user:1001 city
(integer) 1
127.0.0.1:6379> HEXISTS user:1001 city
(integer) 0
127.0.0.1:6379> HLEN user:1001
(integer) 2

典型场景(星选商城)

  • 用户信息user:用户ID,字段如昵称、等级、默认地址 ID。
  • 商品详情字段product:商品ID,名称、价、库存等;改库存只 HSET product:2001 stock 119,不必重写整段 String。
  • 购物车一行:也可用 Hash,cart:用户IDfield=商品IDvalue=数量(与 String 存整份 JSON 相比,更新单品数量更省带宽)。

3.3 List(列表)

是什么

List 是 双向链表(逻辑上是有序的字符串列表),可从两头压入、弹出,支持按索引范围读取。适合 FIFO 队列最新 N 条 时间线。

  key: feed:user:1001
  链表头部 ◄── LPUSH ── 新动态


   [ A , B , C , D ]  ──► LRANGE 0 9 取最新 10 条


  链表尾部 ◄── RPUSH

常用命令(redis-cli 示例)

命令作用
LPUSH / RPUSH从左侧或右侧插入元素
LPOP / RPOP从左侧或右侧弹出
LRANGE按索引范围查看(闭区间)
LLEN列表长度
BRPOP / BLPOP阻塞式弹出,可做简单队列消费者

发动态、看时间线

text
127.0.0.1:6379> DEL feed:user:1001
(integer) 1
127.0.0.1:6379> LPUSH feed:user:1001 "post:501" "post:502" "post:503"
(integer) 3
127.0.0.1:6379> LRANGE feed:user:1001 0 -1
1) "post:503"
2) "post:502"
3) "post:501"
127.0.0.1:6379> LLEN feed:user:1001
(integer) 3

简单消息队列(生产者 / 消费者)

text
127.0.0.1:6379> DEL mq:order:notify
(integer) 1
127.0.0.1:6379> RPUSH mq:order:notify "order:9001:paid" "order:9002:paid"
(integer) 2
127.0.0.1:6379> LPOP mq:order:notify
"order:9001:paid"
127.0.0.1:6379> LPOP mq:order:notify
"order:9002:paid"

阻塞消费(另开一个终端演示更直观)

终端 A:

text
127.0.0.1:6379> BRPOP mq:order:notify 0

终端 B 推入一条后,A 会收到类似:

text
1) "mq:order:notify"
2) "order:9003:paid"

0 表示一直阻塞直到有数据(秒数可改为超时时间)。

典型场景(星选商城)

  • 消息队列:订单支付成功后的异步通知、发券、日志推送(轻量场景;高可靠需 Kafka 等)。
  • 最新动态:用户主页 feed:user:IDLPUSH + LTRIM 0 199 可只保留最近 200 条 ID。

3.4 Set(集合)

是什么

Set 是 无序、不重复 的字符串集合。交集、并集、差集运算原生支持,适合 标签、好友、去重

  tag:数码 = { phone, audio, laptop }
  tag:音频 = { audio, speaker }

  SINTER → { audio }     共同标签
  SUNION → { phone, audio, laptop, speaker }

常用命令(redis-cli 示例)

命令作用
SADD添加成员(自动去重)
SMEMBERS列出所有成员
SISMEMBER判断是否成员
SINTER / SUNION / SDIFF交、并、差
SCARD集合大小

商品标签

text
127.0.0.1:6379> SADD tag:数码 phone audio laptop
(integer) 3
127.0.0.1:6379> SADD tag:音频 audio speaker
(integer) 2
127.0.0.1:6379> SMEMBERS tag:数码
1) "phone"
2) "laptop"
3) "audio"
127.0.0.1:6379> SISMEMBER tag:数码 audio
(integer) 1

共同兴趣 / 共同好友(交集)

text
127.0.0.1:6379> SADD user:1001:follows 2002 2003 2005
(integer) 3
127.0.0.1:6379> SADD user:1002:follows 2003 2005 2008
(integer) 3
127.0.0.1:6379> SINTER user:1001:follows user:1002:follows
1) "2003"
2) "2005"
127.0.0.1:6379> SUNION user:1001:follows user:1002:follows
1) "2002"
2) "2003"
3) "2005"
4) "2008"
127.0.0.1:6379> SDIFF user:1001:follows user:1002:follows
1) "2002"
127.0.0.1:6379> SCARD user:1001:follows
(integer) 3

典型场景(星选商城)

  • 标签系统tag:类目名 或反向 product:2001:tags 用 Set 存标签集合。
  • 共同关注:两个用户关注 Set 做 SINTER
  • 去重:如「今日已领取优惠券的用户 ID」用 SADD coupons:daily:618 userIdSISMEMBER 判断是否领过。

3.5 Sorted Set(有序集合,ZSet)

是什么

ZSet 与 Set 一样 成员不重复,但每个成员带一个 分数(score,双精度浮点)。Redis 按 score 排序,可 按排名或按分数范围 取数据。适合 排行榜、延时任务(按时间戳当 score)

  key: hot:sales:week
  member    score(销量)
  ─────────────────────
  2001      9800
  2003      8750
  2002      1200

  按 score 从大到小 → 热销榜

常用命令(redis-cli 示例)

命令作用
ZADD添加或更新成员分数
ZRANGE / ZREVRANGE按排名升序 / 降序取(可 WITHSCORES
ZRANK / ZREVRANK成员名次(从 0 开始)
ZSCORE查某成员分数
ZRANGEBYSCORE按分数区间取成员

周销量榜

text
127.0.0.1:6379> ZADD hot:sales:week 1200 product:2001 8750 product:2002 9800 product:2003
(integer) 3
127.0.0.1:6379> ZREVRANGE hot:sales:week 0 2 WITHSCORES
1) "product:2003"
2) "9800"
3) "product:2002"
4) "8750"
5) "product:2001"
6) "1200"

127.0.0.1:6379> ZRANK hot:sales:week product:2002
(integer) 1
127.0.0.1:6379> ZREVRANK hot:sales:week product:2002
(integer) 1
127.0.0.1:6379> ZSCORE hot:sales:week product:2002
"8750"

按分数区间(如延迟队列:score = 到期时间戳)

text
127.0.0.1:6379> ZADD delay:task 1710000000 "refund:9001" 1710003600 "sms:remind"
(integer) 2
127.0.0.1:6379> ZRANGEBYSCORE delay:task 0 1710000000 WITHSCORES
1) "refund:9001"
2) "1710000000"

实际应用中会用「当前时间」作为查询上界,配合 ZPOPMIN 等(进阶)做调度。

典型场景(星选商城)

  • 排行榜:小时榜、周榜、类目榜,member 用 product:IDuser:ID,score 用销量、积分、热度。
  • 延迟队列:score 存执行时间,ZRANGEBYSCORE 拉到期任务(需与消费逻辑配合,避免重复执行)。

本章小结

类型值长什么样核心直觉星选商城中的例子
String一段字符串 / 数字串整块读写,支持过期与原子加减商品详情 JSON 缓存、浏览量、锁占位
Hash字段 → 小值一个对象,按字段更新user:IDproduct:ID 多字段
List有序链表队列、时间线、两端操作订单通知队列、用户动态 ID 列表
Set无序不重复集合成员运算、去重标签、共同关注、领券去重
ZSet不重复成员 + 分数排序排名与区间热销榜、按时间调度的延迟任务

选型口诀:单值缓存用 String;对象多字段用 Hash;排队或时间线用 List;只要唯一与集合运算用 Set;要排序或排名用 ZSet


下一章预告

第四章将离开「单个 key 怎么存」,进入 键空间与实用技巧:通配与扫描(KEYSSCAN)、过期策略与内存淘汰、管道与事务初体验,以及如何在真实项目中 设计 key 命名规范 与避免常见坑。掌握这些,你的 Redis 使用会从「会敲命令」进阶到「能稳态上线」。

坚持是一种品格