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