Redis--基础知识点--27--redis缓存分类树
在 Redis 中存储分类树,通常需要选择合适的数据结构来表现层级关系。以下是使用 字符串(String) 和 哈希(Hash) 两种常见方案的举例说明,结合电商分类场景(如 电子产品 > 手机 > 智能手机 > 品牌
)展开:
方案一:字符串(String)存储路径
数据结构设计
- 键名:
category:path:{node_id}
- 例如:
category:path:1001
- 例如:
- 值:完整分类路径(用分隔符连接)
- 例如:
电子产品>手机>智能手机>苹果
- 例如:
操作示例
-
添加分类
# 添加根节点(电子产品) SET category:path:1001 "电子产品"# 添加子节点(手机) SET category:path:1002 "电子产品>手机"# 添加叶节点(苹果) SET category:path:1005 "电子产品>手机>智能手机>苹果"
-
查询分类
# 获取苹果的完整路径 GET category:path:1005 # 返回 "电子产品>手机>智能手机>苹果"# 查询所有手机相关分类(通过模式匹配) KEYS category:path:1002* # 返回匹配的键(需谨慎使用 KEYS 命令)
-
删除分类
# 删除苹果分类(需同时处理其子节点,此处假设无子节点) DEL category:path:1005
-
遍历分类树
# 查询所有根节点(假设根节点路径不含 ">") SCAN 0 MATCH category:path:* COUNT 100 # 遍历所有键,过滤不含 ">" 的值
优缺点
- 优点:实现简单,路径直观。
- 缺点:
- 查询子节点需解析路径字符串(如通过
STRSPLIT
拆分>
)。 - 更新路径需级联修改所有子节点(如重命名“手机”为“移动设备”,需更新所有子节点路径)。
- 查询子节点需解析路径字符串(如通过
方案二:哈希(Hash)存储层级关系
数据结构设计
- 键名:
category:node:{node_id}
- 字段:
name
: 分类名称parent_id
: 父节点 ID(根节点为0
或nil
)level
: 层级深度(可选)
操作示例
-
添加分类
# 添加根节点(电子产品) HSET category:node:1001 name "电子产品" parent_id 0 level 1# 添加子节点(手机) HSET category:node:1002 name "手机" parent_id 1001 level 2# 添加叶节点(苹果) HSET category:node:1005 name "苹果" parent_id 1003 level 4
-
查询分类
# 获取苹果的父节点 ID HGET category:node:1005 parent_id # 返回 "1003"# 查询手机的所有子节点(需递归查询) # 步骤1:找到手机的节点ID(假设为1002) # 步骤2:查询所有 parent_id=1002 的节点 SCAN 0 MATCH category:node:* COUNT 100 | xargs -I{} redis-cli HGET {} parent_id | grep 1002
-
删除分类
# 删除苹果分类(需同时删除其子节点,此处假设无子节点) DEL category:node:1005
-
遍历分类树
# 查询所有根节点(parent_id=0) SCAN 0 MATCH category:node:* COUNT 100 | xargs -I{} redis-cli HGET {} parent_id | grep 0
优缺点
- 优点:
- 结构清晰,便于查询父子关系。
- 修改分类名称无需级联更新子节点(仅修改当前节点
name
字段)。
- 缺点:
- 查询子节点需递归或多次访问 Redis。
- 需维护
parent_id
和level
字段,增加数据一致性风险。
方案三:优化方案 - 路径枚举 + 哈希
结合两种方案优点,使用 哈希存储属性 + 字符串存储路径枚举:
数据结构设计
- 哈希:
category:node:{node_id}
存储name
和parent_id
。 - 字符串:
category:path:{node_id}
存储完整路径(如电子产品>手机>苹果
)。
操作示例
-
添加分类
# 添加苹果分类 HSET category:node:1005 name "苹果" parent_id 1003 SET category:path:1005 "电子产品>手机>智能手机>苹果"
-
查询路径
# 获取苹果的完整路径 GET category:path:1005
-
查询子节点
# 通过哈希查询父节点 ID,再通过路径枚举匹配子节点 HGET category:node:1003 parent_id # 假设1003是智能手机的节点ID KEYS category:path:1003* # 匹配所有以智能手机路径开头的节点
优缺点
- 优点:
- 路径查询高效(直接通过字符串匹配)。
- 属性修改灵活(通过哈希单独更新)。
- 缺点:
- 数据冗余(同时存储哈希和字符串)。
- 需维护两种数据结构的一致性。
方案四:使用 RedisJSON 存储树形结构
如果 Redis 版本支持 RedisJSON 模块,可直接存储 JSON 树形结构:
数据结构设计
- 键名:
category:tree
- 值:JSON 对象,例如:
{"id": 1001,"name": "电子产品","children": [{"id": 1002,"name": "手机","children": [{"id": 1003,"name": "智能手机","children": [{"id": 1004, "name": "苹果"},{"id": 1005, "name": "华为"}]}]}] }
操作示例
-
添加分类
JSON.SET category:tree . '{"id":1001,"name":"电子产品","children":[{"id":1002,"name":"手机","children":[]}]}'
-
查询子节点
JSON.GET category:tree $.children[0].children # 返回手机分类的子节点
-
更新分类
JSON.SET category:tree $.children[0].children[0].name '移动设备' # 重命名手机为移动设备
优缺点
- 优点:
- 结构自然,支持嵌套查询。
- 减少数据冗余(单个键存储完整树)。
- 缺点:
- 需 Redis 版本 ≥ 4.0 且安装 RedisJSON 模块。
- 修改深层节点需精确 JSON 路径(如
$.children[0].children[0].name
)。
总结与选型建议
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
字符串路径 | 简单分类树,查询需求少 | 实现简单,路径直观 | 更新路径需级联修改 |
哈希层级 | 需频繁查询父子关系 | 结构清晰,修改灵活 | 查询子节点需递归 |
路径枚举+哈希 | 平衡路径查询与属性修改 | 路径查询高效,属性修改灵活 | 数据冗余,维护复杂 |
RedisJSON 树形 | 复杂分类树,需嵌套查询 | 结构自然,支持复杂操作 | 依赖模块,路径操作复杂 |
推荐方案:
- 简单场景:使用 字符串路径 或 哈希层级。
- 中等复杂度:使用 路径枚举+哈希。
- 复杂场景:使用 RedisJSON 树形(需确保环境支持)。