千万条数据,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;

 

posted @ 2026-08-05 17:17  民工黑猫  阅读(97)  评论(0)    收藏  举报