You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Redis缓存中实现类文件夹层级结构?(C# WebAPI场景)

嘿,我太懂你这种需求了——Redis里能有类似文件夹的层级缓存结构,不管是查询、清理还是维护都会顺手太多,总比那种东拼西凑的树形键好打理。结合C# WebAPI的场景,我给你几个实用的实现方案,都是我实际项目里用过的,靠谱得很:

方案1:键命名规范 + SCAN命令(最轻量化)

这是最简单的实现方式,不用额外的数据结构,就是给Redis键加上层级前缀,用冒号:分隔,比如你示例里的结构可以写成:

  • a:key1
  • b:c:key2
  • b:c:key3
  • d:...

这种方式的核心是用Redis的SCAN命令匹配前缀,来模拟“打开文件夹”获取所有子键的操作。在C#里用StackExchange.Redis库的话,代码大概是这样:

代码示例

using StackExchange.Redis;

public class RedisHierarchyCache
{
    private readonly IDatabase _db;

    public RedisHierarchyCache(IConnectionMultiplexer redis)
    {
        _db = redis.GetDatabase();
    }

    // 存储键值对,自动带上层级前缀
    public void Set(string hierarchyPath, string key, string value, TimeSpan? expiry = null)
    {
        var fullKey = $"{hierarchyPath}:{key}";
        _db.StringSet(fullKey, value, expiry);
    }

    // 获取某个层级下的所有子键
    public IEnumerable<string> GetAllKeysInHierarchy(string hierarchyPath)
    {
        var prefix = string.IsNullOrEmpty(hierarchyPath) ? "" : $"{hierarchyPath}:";
        var server = _db.Multiplexer.GetServer(_db.Multiplexer.GetEndPoints().First());
        
        // SCAN命令遍历匹配前缀的键,避免KEYS命令的性能问题
        foreach (var key in server.Keys(pattern: $"{prefix}*"))
        {
            yield return key.ToString();
        }
    }

    // 删除整个层级下的所有键
    public void DeleteHierarchy(string hierarchyPath)
    {
        var keys = GetAllKeysInHierarchy(hierarchyPath).ToList();
        if (keys.Any())
        {
            _db.KeyDelete(keys.Select(k => (RedisKey)k).ToArray());
        }
    }
}

注意点

  • 用SCAN而不是KEYS,因为KEYS会阻塞Redis,生产环境绝对不能用
  • 如果层级很深,前缀匹配的效率依然不错,适合中小规模的缓存场景

方案2:哈希表(Hash)存储层级节点(更结构化)

如果想要更“正经”的层级结构,可以把每个“文件夹”当成一个Hash表,Hash的键就是文件夹路径,Hash的字段就是这个文件夹下的子键(或者子文件夹),值可以是缓存内容,或者子文件夹的标识。

比如对于b:c这个文件夹,我们创建一个Hash键b:c,字段是key2、key3,对应的值就是缓存内容。这样要获取整个文件夹的内容,直接HGETALL就能拿到,不用遍历所有键。

代码示例

public void SetInHashHierarchy(string folderPath, string key, string value, TimeSpan? expiry = null)
{
    var hashKey = folderPath;
    _db.HashSet(hashKey, key, value);
    // 给整个文件夹设置过期时间(可选)
    if (expiry.HasValue)
    {
        _db.KeyExpire(hashKey, expiry.Value);
    }
}

// 获取整个文件夹的所有缓存内容
public Dictionary<string, string> GetFolderContent(string folderPath)
{
    var hashEntries = _db.HashGetAll(folderPath);
    return hashEntries.ToDictionary(entry => entry.Name.ToString(), entry => entry.Value.ToString());
}

// 删除某个文件夹下的单个键
public void DeleteKeyInFolder(string folderPath, string key)
{
    _db.HashDelete(folderPath, key);
}

注意点

  • Hash表的单个键最多能存2^32-1个字段,足够大部分场景用
  • 适合缓存同层级的结构化数据,比如某个用户下的所有订单缓存

方案3:有序集合(Sorted Set)维护层级路径(支持复杂查询)

如果需要更复杂的层级操作,比如获取某个节点的所有父节点、子节点,或者递归遍历整个树,可以用Sorted Set来存储所有层级路径,分数可以设为路径的深度或者排序值。

比如把所有路径存进去:a、b、b:c、b:c:key2、b:c:key3、d,然后用ZRANGEBYLEX命令来匹配前缀,获取所有子节点。

代码示例

public void AddHierarchyPath(string path)
{
    // 用路径作为成员,分数设为路径的深度(用冒号分割的数量)
    var depth = path.Split(':').Length;
    _db.SortedSetAdd("hierarchy_paths", path, depth);
}

// 获取某个节点的所有子节点(递归)
public IEnumerable<string> GetAllChildren(string parentPath)
{
    var prefix = string.IsNullOrEmpty(parentPath) ? "" : $"{parentPath}:";
    // 用字典序匹配前缀的所有路径
    var range = _db.SortedSetRangeByLex("hierarchy_paths", $"[{prefix}", $"[{prefix}\xff");
    return range.Select(r => r.ToString());
}

注意点

  • 这种方式适合需要频繁做层级遍历、路径查询的场景
  • 需要额外维护这个Sorted Set,每次添加/删除缓存键的时候都要同步更新路径

最后,我个人最推荐方案1,因为它最简单,不需要额外维护数据结构,而且完全满足你要的文件夹层级感,在大部分WebAPI场景下足够用了。如果你的缓存结构特别复杂,再考虑方案2或3。

内容的提问来源于stack exchange,提问作者Ali_Nass

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:02:06