返回首页

Reds入门1.2: String 与 Hash

适用版本:Redis 7.2.x

学 Redis 时,最容易上手的两个数据结构就是 StringHash。它们出现频率非常高:验证码、计数器、缓存 JSON、用户资料、配置项、购物车摘要……很多看起来不同的业务,底层都可能落在这两类结构上。

很多初学者的问题不在于“命令不会写”,而在于不知道:

  • 什么场景该用 String,什么场景该用 Hash?
  • 一个对象到底是整块存成 String,还是拆字段存成 Hash?
  • 什么时候要加过期时间?
  • 常见命令除了 SETGET 之外,还有哪些必须掌握?

这篇文章就围绕这两个基础结构展开,尽量用“业务含义 + 命令示例 + 实践建议”的方式讲清楚。


一、先理解:Redis 的 String 不只是“字符串”

看到 String 这个名字,很多人会以为它只能存文本。实际上 Redis 的 String 更准确地说,是一种二进制安全的值类型,既可以存:

  • 普通文本
  • 数字
  • JSON 字符串
  • 序列化后的对象
  • Token、验证码、标识符

例如下面这些都可以是 String:

SET name "alice"
SET age 18
SET user:1001 '{"id":1001,"name":"Alice"}'
SET login:token:abc123 "user1001"

所以在 Redis 里,String 的使用范围远比“字符串”这个词表面看起来大得多。


二、String 的核心使用场景

1. 缓存单个值

例如缓存某个配置项:

SET config:site:name "DemoMall"
GET config:site:name

2. 缓存对象 JSON

把对象整体序列化为 JSON 放进去:

SET user:1001 '{"id":1001,"name":"Alice","city":"Beijing"}'
GET user:1001

这种方式最直接,读取时也方便一次取全。

3. 计数器

Redis 非常经典的场景之一。

SET page:view 0
INCR page:view
INCRBY page:view 100
GET page:view

4. 短时令牌或验证码

SET sms:code:13800000000 "483921" EX 300
GET sms:code:13800000000
TTL sms:code:13800000000

因为验证码天然有时效,String + 过期时间是非常自然的组合。


三、String 常用命令详解

1. SET

设置一个 key 的值。

SET name "redis"

返回:

OK

2. GET

获取 key 的值。

GET name

如果 key 不存在,会返回:

(nil)

3. SETEX / SET ... EX

Redis 7 中更常见写法是直接使用 SET key value EX seconds

SET login:code "825631" EX 60

含义:60 秒后自动过期。

也可以指定毫秒:

SET task:flag "1" PX 1500

4. SETNX

只在 key 不存在时设置成功。

SETNX order:lock "1"

这个语义在“抢占式设置”场景很常见,例如简单锁标记。

5. MSET / MGET

一次设置多个值:

MSET name "alice" city "shanghai" role "admin"

一次获取多个值:

MGET name city role

6. INCR / DECR / INCRBY

对数值型字符串执行原子加减。

SET counter 10
INCR counter
DECR counter
INCRBY counter 20

这些命令的价值不只是“写法方便”,更重要的是它们是原子操作,适合高并发计数。

7. APPEND

向原有字符串后追加内容。

SET log "start"
APPEND log ":step1"
GET log

8. STRLEN

获取字符串长度。

SET article:title "Redis 入门"
STRLEN article:title

四、String 使用时最容易踩的点

1. String 能存对象,但对象更新不够细粒度

如果你把一个用户对象直接存成 JSON:

SET user:1001 '{"id":1001,"name":"Alice","age":20}'

当你只想修改 age 时,通常需要:

  1. 先读出整个 JSON
  2. 在业务代码里修改字段
  3. 再整体写回

这没问题,但在字段较多、更新频繁时会显得不够灵活。

2. 数值操作要求值本身可解释为数字

下面这种就会报错:

SET counter "abc"
INCR counter

因为 INCR 只能用于数值内容。

3. 不要忘记过期策略

很多缓存问题,不是“命令不会”,而是“写入时忘了设置 TTL”,最后导致:

  • 缓存永久不失效
  • 脏数据长期存在
  • 内存不断增长

所以要习惯问自己:

这个 key 是长期数据,还是临时数据?

如果是临时数据,通常应该显式设置过期时间。


五、Hash 是什么

Hash 可以理解为:一个 key 对应一个字段集合

如果 String 更像:

key -> value

那么 Hash 更像:

key -> { field1: value1, field2: value2, field3: value3 }

例如用户资料:

HSET user:1001 name "Alice" age "20" city "Beijing"

这里:

  • user:1001 是 key
  • nameagecity 是 field
  • 对应后面的值是 field value

Hash 特别适合表示“一个对象的多个属性”。


六、Hash 的典型场景

1. 用户资料

HSET user:1001 name "Alice" age "20" city "Beijing"
HGET user:1001 name

2. 商品信息摘要

HSET product:2001 title "机械键盘" price "299" stock "58"

3. 配置对象

HSET app:config theme "dark" lang "zh-CN" timezone "Asia/Shanghai"

4. 购物车中某个商品聚合信息

HSET cart:1001:sku:9001 count "2" checked "1" price "199"

七、Hash 常用命令详解

1. HSET

设置一个或多个 field。

HSET user:1001 name "Alice"
HSET user:1001 age "20" city "Beijing"

也可以一条命令设置多个字段:

HSET user:1002 name "Bob" age "25" city "Shanghai"

2. HGET

获取某个 field 的值。

HGET user:1001 name

3. HMGET

一次获取多个字段。

HMGET user:1001 name age city

4. HGETALL

获取整个 Hash 的所有 field 和 value。

HGETALL user:1001

学习阶段很好用,但在字段很多时不应滥用,因为会把整份数据都取出来。

5. HKEYS / HVALS

只获取字段名:

HKEYS user:1001

只获取字段值:

HVALS user:1001

6. HEXISTS

判断某个字段是否存在。

HEXISTS user:1001 age

7. HDEL

删除字段。

HDEL user:1001 city

8. HLEN

统计字段数量。

HLEN user:1001

9. HINCRBY

对 Hash 内某个数值字段执行递增。

HSET article:100 likes 10
HINCRBY article:100 likes 1
HGET article:100 likes

这个命令在“对象里的某个计数字段”场景非常有用。


八、String 和 Hash 到底怎么选

这是入门阶段非常关键的问题。

方案一:对象整体存 String(通常是 JSON)

SET user:1001 '{"id":1001,"name":"Alice","age":20,"city":"Beijing"}'

优点:

  • 结构直观
  • 一次读写完整对象
  • 与很多语言里的序列化/反序列化流程天然兼容

缺点:

  • 单字段修改不方便
  • 读写粒度偏粗
  • 很多场景需要整块覆盖

方案二:对象拆成 Hash

HSET user:1001 id "1001" name "Alice" age "20" city "Beijing"

优点:

  • 便于按字段读写
  • 适合部分更新
  • 某些字段可以单独计数或判断存在性

缺点:

  • 客户端对象映射稍复杂
  • 如果经常一次性取整对象,仍要组织字段

一个很实用的判断原则

  • 以整体读写为主:优先考虑 String(JSON)
  • 以字段级更新为主:优先考虑 Hash
  • 需要简单计数、验证码、token:优先 String
  • 需要表达对象属性结构:优先 Hash

九、过期时间与 Hash 的关系

很多人第一次用 Hash 会问:能不能给 Hash 的某个 field 单独设置过期时间?

在常规用法里,Redis 的过期时间是设置在 key 级别,不是 field 级别。

例如:

HSET user:1001 name "Alice" age "20"
EXPIRE user:1001 3600
TTL user:1001

这表示 user:1001 这个整个 Hash 在 1 小时后过期,而不是某个字段单独过期。

所以设计数据模型时要提前想好:

  • 是整份对象一起过期?
  • 还是某些字段需要独立生命周期?

如果生命周期完全不同,往往应该拆成不同的 key,而不是塞在同一个 Hash 里。


十、完整练习:从零操作一遍

下面给出一组适合自己动手练习的命令。

1. String 练习

SET site:name "RedisNote"
GET site:name

SET sms:code "381926" EX 120
GET sms:code
TTL sms:code

SET page:view 100
INCR page:view
INCRBY page:view 20
GET page:view

2. Hash 练习

HSET user:2001 name "Tom" age "22" city "Hangzhou"
HGET user:2001 name
HMGET user:2001 name age city
HGETALL user:2001
HEXISTS user:2001 age
HINCRBY user:2001 age 1
HGET user:2001 age
EXPIRE user:2001 600
TTL user:2001

练习时建议每敲一条命令,都先猜一下结果,再执行验证,这样记得更牢。


十一、实践建议

1. 统一 key 命名风格

推荐使用带业务前缀的命名方式,例如:

  • user:1001
  • product:2001
  • sms:code:13800000000
  • page:view:home

这样更利于排查和管理。

2. 临时数据优先带 TTL

例如验证码、登录 token、一次性标记,不要写进去就忘了。

3. 不要把 Hash 当“关系数据库表”来用

Hash 很适合表示一个对象,但不适合承载复杂查询需求。如果你已经开始依赖:

  • 多条件筛选
  • 范围查询
  • 关联查询

那往往说明这部分核心数据更适合放数据库,而 Redis 只保留缓存或加速层数据。

4. 读取粒度要克制

如果只需要用户昵称,不要每次都 HGETALL;如果只需要某个配置项,不要把大对象全部拿出来。命令选择本身也会影响性能。


十二、小结

这篇文章围绕 Redis 最基础也最常用的两类结构展开:

  • String:适合单值、JSON、计数器、验证码、token
  • Hash:适合对象字段集合、按属性读写的业务场景

需要重点记住的不是命令数量,而是使用思路:

  1. String 并不只是普通字符串,而是 Redis 最通用的值结构。
  2. Hash 非常适合表示“一个对象的多个属性”。
  3. 对象整体读写多,用 String(JSON) 会更直接。
  4. 字段级修改频繁,用 Hash 更灵活。
  5. TTL 是设计 Redis 数据时必须一起考虑的问题。

把 String 和 Hash 学扎实,后面再看 List、Set、ZSet,会更容易建立完整的数据结构选型思路。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

Redis入门1.1: 简介与安装

下一篇

Redis入门1.3: List、Set、ZSet 详解