Redis--基础知识点--27--redis缓存分类树

发布于:2025-05-17 ⋅ 阅读:(22) ⋅ 点赞:(0)

在 Redis 中存储分类树,通常需要选择合适的数据结构来表现层级关系。以下是使用 字符串(String)哈希(Hash) 两种常见方案的举例说明,结合电商分类场景(如 电子产品 > 手机 > 智能手机 > 品牌)展开:


方案一:字符串(String)存储路径

数据结构设计
  • 键名category:path:{node_id}
    • 例如:category:path:1001
  • :完整分类路径(用分隔符连接)
    • 例如:电子产品>手机>智能手机>苹果
操作示例
  1. 添加分类

    # 添加根节点(电子产品)
    SET category:path:1001 "电子产品"
    
    # 添加子节点(手机)
    SET category:path:1002 "电子产品>手机"
    
    # 添加叶节点(苹果)
    SET category:path:1005 "电子产品>手机>智能手机>苹果"
    
  2. 查询分类

    # 获取苹果的完整路径
    GET category:path:1005  # 返回 "电子产品>手机>智能手机>苹果"
    
    # 查询所有手机相关分类(通过模式匹配)
    KEYS category:path:1002*  # 返回匹配的键(需谨慎使用 KEYS 命令)
    
  3. 删除分类

    # 删除苹果分类(需同时处理其子节点,此处假设无子节点)
    DEL category:path:1005
    
  4. 遍历分类树

    # 查询所有根节点(假设根节点路径不含 ">")
    SCAN 0 MATCH category:path:* COUNT 100  # 遍历所有键,过滤不含 ">" 的值
    
优缺点
  • 优点:实现简单,路径直观。
  • 缺点
    • 查询子节点需解析路径字符串(如通过 STRSPLIT 拆分 >)。
    • 更新路径需级联修改所有子节点(如重命名“手机”为“移动设备”,需更新所有子节点路径)。

方案二:哈希(Hash)存储层级关系

数据结构设计
  • 键名category:node:{node_id}
  • 字段
    • name: 分类名称
    • parent_id: 父节点 ID(根节点为 0nil
    • level: 层级深度(可选)
操作示例
  1. 添加分类

    # 添加根节点(电子产品)
    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
    
  2. 查询分类

    # 获取苹果的父节点 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
    
  3. 删除分类

    # 删除苹果分类(需同时删除其子节点,此处假设无子节点)
    DEL category:node:1005
    
  4. 遍历分类树

    # 查询所有根节点(parent_id=0)
    SCAN 0 MATCH category:node:* COUNT 100 | xargs -I{} redis-cli HGET {} parent_id | grep 0
    
优缺点
  • 优点
    • 结构清晰,便于查询父子关系。
    • 修改分类名称无需级联更新子节点(仅修改当前节点 name 字段)。
  • 缺点
    • 查询子节点需递归或多次访问 Redis。
    • 需维护 parent_idlevel 字段,增加数据一致性风险。

方案三:优化方案 - 路径枚举 + 哈希

结合两种方案优点,使用 哈希存储属性 + 字符串存储路径枚举

数据结构设计
  • 哈希category:node:{node_id} 存储 nameparent_id
  • 字符串category:path:{node_id} 存储完整路径(如 电子产品>手机>苹果)。
操作示例
  1. 添加分类

    # 添加苹果分类
    HSET category:node:1005 name "苹果" parent_id 1003
    SET category:path:1005 "电子产品>手机>智能手机>苹果"
    
  2. 查询路径

    # 获取苹果的完整路径
    GET category:path:1005
    
  3. 查询子节点

    # 通过哈希查询父节点 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": "华为"}
              ]
            }
          ]
        }
      ]
    }
    
操作示例
  1. 添加分类

    JSON.SET category:tree . '{"id":1001,"name":"电子产品","children":[{"id":1002,"name":"手机","children":[]}]}'
    
  2. 查询子节点

    JSON.GET category:tree $.children[0].children  # 返回手机分类的子节点
    
  3. 更新分类

    JSON.SET category:tree $.children[0].children[0].name '移动设备'  # 重命名手机为移动设备
    
优缺点
  • 优点
    • 结构自然,支持嵌套查询。
    • 减少数据冗余(单个键存储完整树)。
  • 缺点
    • 需 Redis 版本 ≥ 4.0 且安装 RedisJSON 模块。
    • 修改深层节点需精确 JSON 路径(如 $.children[0].children[0].name)。

总结与选型建议

方案 适用场景 优点 缺点
字符串路径 简单分类树,查询需求少 实现简单,路径直观 更新路径需级联修改
哈希层级 需频繁查询父子关系 结构清晰,修改灵活 查询子节点需递归
路径枚举+哈希 平衡路径查询与属性修改 路径查询高效,属性修改灵活 数据冗余,维护复杂
RedisJSON 树形 复杂分类树,需嵌套查询 结构自然,支持复杂操作 依赖模块,路径操作复杂

推荐方案

  • 简单场景:使用 字符串路径哈希层级
  • 中等复杂度:使用 路径枚举+哈希
  • 复杂场景:使用 RedisJSON 树形(需确保环境支持)。