需求:每秒1万个http请求,响应时间500毫秒以内。
1万QPS的缓存实现方案
分3个场景:
场景1:单ID详情查询
按ID存储到Redis分片,可以用3台Redis分片。
场景2:列表分面产,多条件筛选(千万条数据)
方案1:使用ReidsSearch,支持按多条件、分页筛选数据,使用3个Redis分片节点。
方案2:使用ElasticSearch,支持安多条件、分页筛选数据,使用3个ElasticSearch分片节点。
场景3:树形数据,需要递归查询(10万条数据)
这种情况不能使用Redis或ElasticSearch做缓存了。因为要10万条数据做递归查询一个树型,不能单条读Redis或ES,也不能把一个10万条数据的json字符串存到Redis。这种情况只能使用内存缓存了,把10万条数据存到内存。
有三个方案:
方案1:把整条List存到MemoryCache,在内存里递归查询,部署3台api节点;
方案2:不用递归,用本地词典,部署3台api节点,性能最高。
扁平化预计算树形关系,内存中直接建立索引,无需递归查询。
不存原始树形嵌套对象,内存维护 3 套字典索引,O (1) 全量遍历、父子 / 子孙查询:
// 1. 全节点主键字典:ID → 节点完整实体
Dictionary<long, TreeItem> AllNodeDict;
// 2. 父级索引:父ID → 子节点ID列表(查直接子节点O(1))
Dictionary<long, List<long>> ChildIndex;
// 3. 祖先链路索引:节点ID → 根到自身全路径ID数组(递归向上查询直接取)
Dictionary<long, List<long>> ParentChainIndex;
// 4. 后代全量索引:节点ID → 所有子孙ID(一次性查整棵子树,无需循环递归)
Dictionary<long, HashSet<long>> DescendantIndex;
优势
全部数据驻留应用进程内存,无任何网络 IO,单次树形查询微秒级,单实例轻松承载 2~5 万 QPS,满足 1 万 QPS 需求。
提前预计算子孙、祖先关系,业务代码完全不用递归循环查询缓存 / DB。
10 万条树形数据内存占用极低,普通服务器内存无压力。
单实例重启:仅冷启动瞬间拉一次全量数据。
方案3:用RockDB,嵌入式混合冷热缓存(几十万条数据,内存放不下)
关键矛盾:
10 万~几十万树形数据,纯全量驻内存会占用过高,但完全放远程 Redis 递归查询网络 IO 爆炸;
嵌入式 RocksDB 采用「内存 BlockCache + 磁盘持久层」冷热分离,自动热点放内存、冷节点落本地磁盘,不用一次性把几十万条全塞进内存,完美平衡内存占用与查询性能。