第三章 五大基础数据类型
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 不存在时才设置(常用于锁) |
基础读写
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>"计数器(原子操作,并发安全)
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过期与缓存
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) 7199SETNX 与简单分布式锁思路
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。 - 分布式锁:订单支付、库存扣减等互斥操作,用
SETNX或SET ... 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 | 字段个数 |
存用户资料(对象)
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"删除与存在性
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:用户ID里field=商品ID,value=数量(与 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 | 阻塞式弹出,可做简单队列消费者 |
发动态、看时间线
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简单消息队列(生产者 / 消费者)
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:
127.0.0.1:6379> BRPOP mq:order:notify 0终端 B 推入一条后,A 会收到类似:
1) "mq:order:notify"
2) "order:9003:paid"0 表示一直阻塞直到有数据(秒数可改为超时时间)。
典型场景(星选商城)
- 消息队列:订单支付成功后的异步通知、发券、日志推送(轻量场景;高可靠需 Kafka 等)。
- 最新动态:用户主页
feed:user:ID,LPUSH+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 | 集合大小 |
商品标签
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共同兴趣 / 共同好友(交集)
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 userId,SISMEMBER判断是否领过。
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 | 按分数区间取成员 |
周销量榜
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 = 到期时间戳)
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:ID或user:ID,score 用销量、积分、热度。 - 延迟队列:score 存执行时间,
ZRANGEBYSCORE拉到期任务(需与消费逻辑配合,避免重复执行)。
本章小结
| 类型 | 值长什么样 | 核心直觉 | 星选商城中的例子 |
|---|---|---|---|
| String | 一段字符串 / 数字串 | 整块读写,支持过期与原子加减 | 商品详情 JSON 缓存、浏览量、锁占位 |
| Hash | 字段 → 小值 | 一个对象,按字段更新 | user:ID、product:ID 多字段 |
| List | 有序链表 | 队列、时间线、两端操作 | 订单通知队列、用户动态 ID 列表 |
| Set | 无序不重复集合 | 成员运算、去重 | 标签、共同关注、领券去重 |
| ZSet | 不重复成员 + 分数排序 | 排名与区间 | 热销榜、按时间调度的延迟任务 |
选型口诀:单值缓存用 String;对象多字段用 Hash;排队或时间线用 List;只要唯一与集合运算用 Set;要排序或排名用 ZSet。
下一章预告
第四章将离开「单个 key 怎么存」,进入 键空间与实用技巧:通配与扫描(KEYS 与 SCAN)、过期策略与内存淘汰、管道与事务初体验,以及如何在真实项目中 设计 key 命名规范 与避免常见坑。掌握这些,你的 Redis 使用会从「会敲命令」进阶到「能稳态上线」。