哈希
一、认识
在 Redis
中,hash
(哈希)是一种用于存储 键值对集合 的数据类型,类似于一个小型的 key-value
字典(对象)。它特别适合存储具有多个属性的对象,例如用户信息、产品详情等。在 Redis
中,哈希 是以字段(field
)和值(value
)的形式存储在一个主键(key
)下的。例如,可以使用 hash
来存储用户的信息,如 user:1001
,并包含多个字段:name
、age
、email
等。
Redis Hash
存储结构类似于 JSON
对象或字典。比如: user:1001 → { "name": "Alice", "age": "25", "email": "alice@example.com" }
。Redis Hash
高效存储, 如果哈希中的字段数量较少(小于 512
个字段,字段和值总大小不超过 64KB
),Redis
会使用特殊的 ziplist
(压缩列表) 实现,节省内存。Redis
适合存储结构化数据, 可用于存储对象数据,如用户信息、配置项等,避免使用多个字符串键。Redis Hash
支持部分字段操作,避免整体操作, 可以只修改、获取某个字段,而不影响整个哈希数据。Redis
字段增量操作支持, Redis
提供对数值字段的原子递增/递减操作,方便计数操作。
Redis Hash
适用于存储结构化数据,如果你的数据像对象那样包含多个属性,使用 Hash
更合适。比如说: 用户信息存储; 对象属性操作CRUD
, 需要频繁更新或获取对象的部分字段,例如商品库存、价格信息等; 动态配置管理, 需要存储多种配置参数并且可能会经常调整,比如应用配置、用户偏好设置等; 排行榜存储, 存储用户游戏统计数据,如分数、等级、胜场等,方便查询和更新; 避免键的爆炸, 如果你的数据非常多且字段固定,使用 Hash
可以减少 Redis
的键数量,避免单独存储多个 String
类型带来的管理负担。在存储大量类似数据时,避免使用大量独立的 String
类型键,而是将数据组织成 Hash
类型,以减少 Redis
管理的键数量,提高查询效率, 降低键的数量,避免“键爆炸”。合理使用哈希字段,避免过大对象, 虽然 Hash
可以存储大量字段,但单个 Hash
过大可能导致:LRU
淘汰策略时,整个 Hash
被一次性移除,影响业务稳定性, 迁移、持久化时增加 Redis
负载, 所以, 设计合理的存储粒度,避免单个 Hash
超过 10
万个字段。
Redis Hash
优化策略: Redis
内部会对小型 Hash
采用特殊的优化机制,即压缩列表(ZipList
), 它可以将数据存储在同一个连续的内存块中, 减少内存碎片以减少内存开销, 触发条件为: 字段数量(hash-max-ziplist-entries) ≤ 512(默认值)
, 每个字段和值的大小 (hash-max-ziplist-value) ≤ 64 字节(默认值)
。但是, ZipList
读写有指针位移, 且新增删除有内存重分配。如果数据超出阈值,Redis
将自动切换为标准的 Hash Table
结构,占用更多的内存。
-
调整参数以适应业务需求:
CONFIG SET hash-max-ziplist-entries 1024
CONFIG SET hash-max-ziplist-value 128 -
检查是否启用
ZipList
优化: 如果返回ziplist
,说明正在使用压缩存储。OBJECT ENCODING user:1001
Redis Hash
分片策略: 将大数据拆分到多个 Hash
, 每个小段尽可能符合 ZipList
的优化范围, 因为 ZipList
非常节省内存空间。但是 ZipList
读写有指针位移, 且新增删除有内存重分配, 会有性能问题。所以, 我们权衡之后, 选择第三种最好。
// picId => usesrId, 大约有 100 万 数据量, 有如下三种方案:
set pic:{picId} user:{userId} // 全部 string, 浪费内存
hset allPics pic:{pidId} user:{userId} // 一个 hash, hash allPics 元素个数已经有 100 万多个, 是一个超大的 Big Key, 浪费内存。
hset segment:{picId/100} pic:{picId%100} user:{userId} // hash 分片
// 三种方案的结果, hash 分片消耗的内存是最小的, 且比前两种少了好几倍。一个 hahs 消耗的内存是最多的, 是一个超大的 Big Key。