千万条数据,1万QPS的实现方案
一千万条数据,1万QPS的实现方案。
需求:主表一千万条数据,每秒1万个http请求,响应时间500毫秒以内。
一次接口访问,需要调用多次数据库,所以数据库本身承载不了那么大的QPS的,解决方案只有一种,那就是使用缓存。
分3个场景:
场景 1:单 ID 详情查询(商品 / 基础信息,按主键查询)
1. L1 本地缓存(IMemoryCache / Caffeine)
- 容量限制:限制本地缓存2 万个热点 key,LRU 淘汰,缓存时间10分钟;
- 命中耗时约1 ms,拦截 80% 流量,大幅减少 Redis 网络往返;
- 多实例一致性:Redis Pub/Sub 发布广播消息,所有 API 节点订阅Redis的广播消息,增量刷新本地缓存,无需全量重载。
2. L2 Redis 集群(3 台主分片,每主 1 从,共 6 节点)
- 存储结构:
detail:{id}单条 KV,Protobuf 压缩序列化; - 性能:单从节点支撑 3~5 万读 QPS,3 分片集群轻松承载 1 万 QPS,Redis 单次往返耗时 50ms。
4. DB 兜底策略
场景 2:列表分页 + 多条件筛选(千万级产品表,区间 / 分类 / 排序分页)
两种对等方案,按需选择。
方案 2-1:RedisSearch + Redis Cluster 3 分片集群
存储设计:
每条产品存储为 RedisJSON,product:{id},对分类、价格、创建时间建立全文 / 数值联合索引;
查询能力:
RedisSearch 原生支持多条件过滤、limit offset/size、游标分页、多字段排序,无需多次网络交互,单次命令完成分页检索;
集群部署:
3 台 Redis Master 分片,每节点开启 RedisSearch 模块,数据均匀打散,分片并行检索后合并结果;
方案 2-2:Elasticsearch 3 主分片集群(行业首选)
1. 分片规划
索引设置 3 个主分片,2 副本,3 台 ES 数据节点均匀分布分片;单分片承载 3000 + 列表 QPS,3 分片满足 1 万并发;
2. 分页优化(千万级表必做)
浅分页(前 50 页):from/size;
深度分页:强制 search_after 游标分页,规避 offset 过大扫描性能衰减;
3. 数据同步
同步方案一:数据更新操作,将最终数据写进MQ,MQ消费端更新数据到ES。
同步方案二:Canal 监听 DB Binlog,实时同步全量商品字段至 ES;凌晨全量同步任务修复数据不一致。
场景3:树形数据,父子 / 子孙路径查询(10万条数据)
这种情况不能使用Redis或ElasticSearch做缓存了。因为要10万条数据做递归查询一个树型,不能单条读Redis或ElasticSearch,也不能把一个10万条数据的json字符串存到Redis。这种情况只能使用内存缓存了,把10万条数据存到内存。
内存缓存有三个方案:
方案1:原生 List 内存缓存
站点启动的时候把10万条数据放进List,整条List存到MemoryCache,在内存里递归查询,部署3台api节点;
方案2:纯进程本地扁平化内存索引(性能最优)
API 节点 3 台独立部署,每台启动一次性加载全量树形数据,预构建 4 套内存字典索引,彻底消除运行时递归查询:
// 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;
优势:
直接拿到所有父节点id或子节点id,从全节点字典里查所数据。
多实例分布式一致性方案:
数据更新流程:更新 DB → 使用 Redis Publish 广播更新的数据,所有点节订阅广播消息,收到广播消息更新四个字典。
定时每天全量同步一次数据,修复脏数据。
方案3:用RockDB,嵌入式混合冷热缓存(几十万条数据,内存放不下的情况)
关键矛盾:
10 万~几十万树形数据,全量数据常驻内存会占用过高;
实现机制:
进程内嵌入式 KV 库,全量树形持久化本地 SSD 磁盘,限制内存block_cache=512MB。
热点层级(根、一级菜单、高频查询子树)常驻内存,O (1) 读取。
低频叶子节点存在磁盘,首次访问加载进缓存,超出 512MB 自动淘汰最冷数据,内存不会持续上涨。
数据同步方案:
数据更新流程:更新 DB → 使用 Redis Publish 广播更新的数据,所有点节订阅广播消息,收到广播消息更新RocksDB。
定时每天全量同步一次数据,修复脏数据。
内存参数控制占用内存上限(防止吃满单机内存):
block_cache_size = 512MB; // 热点数据内存上限,固定死,不会无限膨胀 write_buffer_size = 64MB; max_write_buffer_number = 2;

浙公网安备 33010602011771号