适用版本:Redis 7.2.x
学 Redis 时,最容易上手的两个数据结构就是 String 和 Hash。它们出现频率非常高:验证码、计数器、缓存 JSON、用户资料、配置项、购物车摘要……很多看起来不同的业务,底层都可能落在这两类结构上。
很多初学者的问题不在于“命令不会写”,而在于不知道:
- 什么场景该用 String,什么场景该用 Hash?
- 一个对象到底是整块存成 String,还是拆字段存成 Hash?
- 什么时候要加过期时间?
- 常见命令除了
SET、GET之外,还有哪些必须掌握?
这篇文章就围绕这两个基础结构展开,尽量用“业务含义 + 命令示例 + 实践建议”的方式讲清楚。
一、先理解: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 时,通常需要:
- 先读出整个 JSON
- 在业务代码里修改字段
- 再整体写回
这没问题,但在字段较多、更新频繁时会显得不够灵活。
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是 keyname、age、city是 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:1001product:2001sms:code:13800000000page:view:home
这样更利于排查和管理。
2. 临时数据优先带 TTL
例如验证码、登录 token、一次性标记,不要写进去就忘了。
3. 不要把 Hash 当“关系数据库表”来用
Hash 很适合表示一个对象,但不适合承载复杂查询需求。如果你已经开始依赖:
- 多条件筛选
- 范围查询
- 关联查询
那往往说明这部分核心数据更适合放数据库,而 Redis 只保留缓存或加速层数据。
4. 读取粒度要克制
如果只需要用户昵称,不要每次都 HGETALL;如果只需要某个配置项,不要把大对象全部拿出来。命令选择本身也会影响性能。
十二、小结
这篇文章围绕 Redis 最基础也最常用的两类结构展开:
- String:适合单值、JSON、计数器、验证码、token
- Hash:适合对象字段集合、按属性读写的业务场景
需要重点记住的不是命令数量,而是使用思路:
- String 并不只是普通字符串,而是 Redis 最通用的值结构。
- Hash 非常适合表示“一个对象的多个属性”。
- 对象整体读写多,用 String(JSON) 会更直接。
- 字段级修改频繁,用 Hash 更灵活。
- TTL 是设计 Redis 数据时必须一起考虑的问题。
把 String 和 Hash 学扎实,后面再看 List、Set、ZSet,会更容易建立完整的数据结构选型思路。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!