Skip to content

Redis 中 Hash 和 String 的区别

在 Redis 中,StringHash 看似都能存储键值数据,但它们的核心差异在于数据组织方式操作粒度。尽管 String 足够灵活(甚至可以存储序列化的 JSON),Hash 的存在是为了解决以下关键问题。

  • String
    以「整个值」为最小操作单元,适合存储独立数据(如缓存 HTML、计数器)。
Terminal window
SET user:123 '{"name":"Alice","age":30,"city":"NY"}' # 存储整个 JSON
GET user:123 # 必须读取整个值,修改需反序列化→更新→重新序列化
┌───────────────┐ ┌───────────────────────────────┐
│ Key: │ │ │
│ "user:1001" │───────▶ │ Value: "{'name':'Alice', ...}"│
└───────────────┘ └───────────────────────────────┘
  • Hash
    以「字段」为最小操作单元,适合存储结构化对象,支持按需读写部分字段。
Terminal window
HSET user:123 name "Alice" age 30 city "NY" # 独立存储字段
HGET user:123 age # 直接读取年龄(无需解析整个对象)
HINCRBY user:123 age 1 # 原子性递增年龄
┌───────────────┐ ┌───────────────┬───────────────┐
│ Key: │ │ Field: "name"│ Value: "Alice"│
│ "user:1001" │───────▶ ├───────────────┼───────────────┤
└───────────────┘ │ Field: "age" │ Value: 30 │
├───────────────┼───────────────┤
│ Field: "city"│ Value: "NY" │
└───────────────┴───────────────┘
  • String
    每个键独立存储,即使存储同一对象的多个字段,也会因 Redis 的元数据开销(如 key 名称、过期时间)导致内存浪费。
Terminal window
SET user:123:name "Alice" # 独立键,元数据重复存储
SET user:123:age 30
  • Hash
    所有字段共享一个键的元数据,Redis 内部采用高效结构(如 ziplist 或 hashtable)存储,尤其在小规模数据时内存占用更低。
Terminal window
HMSET user:123 name "Alice" age 30 # 单键存储,内存更紧凑
  • String
    无法原子化操作多个字段,需依赖事务或 Lua 脚本。
Terminal window
# 同时更新 name 和 age 需事务保证原子性
MULTI
SET user:123:name "Bob"
SET user:123:age 31
EXEC
  • Hash
    原生支持多字段原子操作,且提供批量读写命令。
Terminal window
HMSET user:123 name "Bob" age 31 # 原子性更新多个字段
HMGET user:123 name age # 一次读取多个字段
  1. “Hash 一定比 String 快”
    若始终需要读写整个对象(如一次性缓存 HTML),String 更高效,因为 Hash 的 HGETALL 需要遍历字段。
  2. “所有结构化数据都该用 Hash”
    若数据字段数量极大(如数百个),Hash 的查询效率可能下降,此时可考虑拆分为多个 Hash 或结合其他结构。