Redis哈希Hash #

一、哈希概述 #

1.1 什么是哈希 #

Redis哈希是一个键值对集合,适合存储对象:

  • 字段-值对:类似Map、Dictionary
  • 适合存储对象:一个键存储多个字段
  • 内存高效:比多个String键更节省内存
text
哈希结构:

┌─────────────────────────────────────────────┐
│  Key: user:1001                             │
│  ┌───────────────────────────────────────┐ │
│  │  field       │  value                 │ │
│  ├──────────────┼────────────────────────┤ │
│  │  name        │  "John"                │ │
│  │  age         │  "25"                  │ │
│  │  email       │  "john@example.com"    │ │
│  │  city        │  "Beijing"             │ │
│  └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

对比String存储:
┌─────────────────────────────────────────────┐
│  String方式:                               │
│  user:1001:name → "John"                    │
│  user:1001:age  → "25"                      │
│  user:1001:email→ "john@example.com"        │
│  user:1001:city → "Beijing"                 │
│  (需要4个键,内存开销更大)                 │
└─────────────────────────────────────────────┘

1.2 哈希编码 #

text
Redis哈希的编码方式:

1. listpack(压缩列表)
   ┌─────────────────────────────────────────────┐
   │ 字段数量少时使用                            │
   │ 条件:字段数<512,单字段值<64字节           │
   │ 内存连续,效率高                            │
   └─────────────────────────────────────────────┘

2. hashtable(哈希表)
   ┌─────────────────────────────────────────────┐
   │ 字段数量多时使用                            │
   │ O(1)时间复杂度                              │
   │ 支持扩容和缩容                              │
   └─────────────────────────────────────────────┘

二、基本操作 #

2.1 设置字段 #

bash
# HSET: 设置单个字段
HSET user:1001 name "John"
# (integer) 1  新增字段数

# 设置多个字段
HSET user:1001 age 25 email "john@example.com"
# (integer) 2

# 更新已有字段
HSET user:1001 name "Mike"
# (integer) 0  更新不计数

# HMSET: 批量设置(已弃用,建议使用HSET)
HMSET user:1002 name "Jane" age 30
# OK

# HSETNX: 字段不存在时设置
HSETNX user:1001 name "John"
# (integer) 0  字段已存在,不设置

HSETNX user:1001 city "Beijing"
# (integer) 1  字段不存在,设置成功

2.2 获取字段 #

bash
# HGET: 获取单个字段
HGET user:1001 name
# "John"

# 获取不存在的字段
HGET user:1001 notexist
# (nil)

# HMGET: 获取多个字段
HMGET user:1001 name age email
# 1) "John"
# 2) "25"
# 3) "john@example.com"

# HGETALL: 获取所有字段和值
HGETALL user:1001
# 1) "name"
# 2) "John"
# 3) "age"
# 4) "25"
# 5) "email"
# 6) "john@example.com"
# 7) "city"
# 8) "Beijing"

# HKEYS: 获取所有字段名
HKEYS user:1001
# 1) "name"
# 2) "age"
# 3) "email"
# 4) "city"

# HVALS: 获取所有值
HVALS user:1001
# 1) "John"
# 2) "25"
# 3) "john@example.com"
# 4) "Beijing"

# HLEN: 获取字段数量
HLEN user:1001
# (integer) 4

2.3 检查字段 #

bash
# HEXISTS: 检查字段是否存在
HEXISTS user:1001 name
# (integer) 1  存在

HEXISTS user:1001 notexist
# (integer) 0  不存在

2.4 删除字段 #

bash
# HDEL: 删除字段
HDEL user:1001 city
# (integer) 1  删除成功

HDEL user:1001 notexist
# (integer) 0  字段不存在

# 删除多个字段
HDEL user:1001 age email
# (integer) 2

三、数值操作 #

3.1 自增自减 #

bash
# HINCRBY: 增加整数值
HSET user:1001 age 25
HINCRBY user:1001 age 1
# (integer) 26

HINCRBY user:1001 age 10
# (integer) 36

HINCRBY user:1001 age -5
# (integer) 31

# 对不存在的字段自增(从0开始)
HINCRBY user:1001 score 10
# (integer) 10

# HINCRBYFLOAT: 增加浮点数
HSET product:1001 price 99.99
HINCRBYFLOAT product:1001 price 0.01
# "100.00"

HINCRBYFLOAT product:1001 price -10.5
# "89.5"

# 对非数值字段操作会报错
HSET user:1001 name "John"
HINCRBY user:1001 name 1
# (error) ERR hash value is not an integer

四、批量操作 #

4.1 批量获取 #

bash
# HMGET: 批量获取多个字段
HMGET user:1001 name age email city
# 1) "John"
# 2) "25"
# 3) "john@example.com"
# 4) (nil)  # 不存在的字段返回nil

4.2 扫描字段 #

bash
# HSCAN: 迭代扫描字段
HSET user:1001 field1 value1 field2 value2 field3 value3

HSCAN user:1001 0
# 1) "0"  # 下一个游标
# 2) 1) "field1"
#    2) "value1"
#    3) "field2"
#    4) "value2"

# 带模式匹配
HSCAN user:1001 0 MATCH field*
# 1) "0"
# 2) 1) "field1"
#    2) "value1"
#    3) "field2"
#    4) "value2"

# 带数量限制
HSCAN user:1001 0 COUNT 2

五、应用场景 #

5.1 存储对象 #

bash
# 用户信息
HSET user:1001 name "John" age 25 email "john@example.com" city "Beijing"

# 获取用户信息
HGETALL user:1001

# 更新单个字段
HSET user:1001 age 26

# 获取单个字段
HGET user:1001 name

5.2 购物车 #

bash
# 添加商品到购物车
# field: 商品ID, value: 数量
HSET cart:user:1001 product:2001 2
HSET cart:user:1001 product:2002 1

# 获取购物车所有商品
HGETALL cart:user:1001
# 1) "product:2001"
# 2) "2"
# 3) "product:2002"
# 4) "1"

# 更新商品数量
HINCRBY cart:user:1001 product:2001 1
# (integer) 3

# 删除商品
HDEL cart:user:1001 product:2002

# 获取购物车商品数量
HLEN cart:user:1001

5.3 计数器 #

bash
# 文章统计
HSET article:1001 views 0 likes 0 comments 0

# 增加阅读量
HINCRBY article:1001 views 1
# (integer) 1

# 增加点赞
HINCRBY article:1001 likes 1
# (integer) 1

# 获取所有统计
HGETALL article:1001

5.4 缓存对象 #

bash
# 缓存用户信息(比JSON更灵活)
HSET cache:user:1001 id 1001 name "John" age 25

# 只更新部分字段
HSET cache:user:1001 age 26

# 只获取需要的字段
HMGET cache:user:1001 name age
# 1) "John"
# 2) "26"

# 检查字段是否存在
HEXISTS cache:user:1001 email
# (integer) 0

5.5 配置管理 #

bash
# 应用配置
HSET config:app theme "dark" language "zh-CN" timezone "Asia/Shanghai"

# 获取配置
HGET config:app theme
# "dark"

# 更新配置
HSET config:app theme "light"

# 获取所有配置
HGETALL config:app

5.6 会话存储 #

bash
# 存储会话信息
HSET session:abc123 user_id 1001 login_time 1700000000 ip "192.168.1.1"

# 更新最后活动时间
HSET session:abc123 last_active 1700001000

# 检查会话
HEXISTS session:abc123 user_id
# (integer) 1

# 删除会话
DEL session:abc123

六、性能优化 #

6.1 选择合适的数据结构 #

bash
# 存储对象:使用Hash
# 推荐
HSET user:1001 name "John" age 25

# 不推荐:使用多个String
SET user:1001:name "John"
SET user:1001:age 25

6.2 避免大哈希 #

bash
# 不推荐:超大哈希
HSET big:hash field1 value1 ... field100000 value100000

# 推荐:分片存储
HSET hash:1 field1 value1 ... field1000 value1000
HSET hash:2 field1001 value1001 ... field2000 value2000

6.3 使用HSCAN代替HGETALL #

bash
# 不推荐:获取所有字段
HGETALL big:hash  # 数据量大时阻塞

# 推荐:迭代扫描
HSCAN big:hash 0

6.4 批量操作 #

bash
# 不推荐:多次单操作
HSET user:1001 name "John"
HSET user:1001 age 25
HSET user:1001 email "john@example.com"

# 推荐:批量操作
HSET user:1001 name "John" age 25 email "john@example.com"

七、Hash vs String #

7.1 内存对比 #

text
存储用户信息(4个字段):

String方式:
┌─────────────────────────────────────────────┐
│ user:1001:name → "John"                     │
│ user:1001:age  → "25"                       │
│ user:1001:email→ "john@example.com"         │
│ user:1001:city → "Beijing"                  │
│                                             │
│ 内存开销:                                   │
│ - 4个键的开销                                │
│ - 4个RedisObject开销                        │
│ - 约200+字节                                 │
└─────────────────────────────────────────────┘

Hash方式:
┌─────────────────────────────────────────────┐
│ user:1001 → {                               │
│   name: "John",                             │
│   age: "25",                                │
│   email: "john@example.com",                │
│   city: "Beijing"                           │
│ }                                           │
│                                             │
│ 内存开销:                                   │
│ - 1个键的开销                                │
│ - 1个RedisObject开销                        │
│ - 约100字节                                  │
└─────────────────────────────────────────────┘

7.2 使用场景对比 #

场景 推荐类型 原因
简单缓存 String 简单直接
对象存储 Hash 内存效率高
需要部分更新 Hash 只更新单个字段
需要过期单个字段 不支持 Redis不支持字段级过期
计数器 String或Hash 都可以

八、总结 #

哈希操作命令:

命令 说明
HSET 设置字段
HGET 获取字段
HMSET 批量设置
HMGET 批量获取
HGETALL 获取所有
HDEL 删除字段
HEXISTS 检查字段
HKEYS 获取所有字段名
HVALS 获取所有值
HLEN 获取字段数量
HINCRBY 自增整数
HINCRBYFLOAT 自增浮点数

应用场景:

场景 实现方式
对象存储 HSET + HGETALL
购物车 HSET + HINCRBY
计数器 HINCRBY
配置管理 HSET + HGET

下一步,让我们学习Redis有序集合ZSet!

最后更新:2026-03-27