本文详细描述如何实现:目前手上可用的资源仅剩一个 16 核剩余 4-8G 内存的机器,单点完成在 1 个小时内千万级别 feed 流数据 flush 操作(主要包括:读数据,计算综合得分,淘汰低分数据,并更新最新得分,回写缓存和数据库)。
背景
目前工作负责的一款产品增加了综合得分序的 Feed 流排序方式:需要每天把(将近 1000W 数据量)的 feed 流信息进行算分计算更新后回写到数据层。手上的批跑物理机器是 16 核(因为混部,无法独享 CPU),同时剩下可用内存仅 4-8G。显而易见的是:我们可以申请机器,多机部署,分片计算或者通过现有的大数据平台 Hadoop 进行运算都看似可以解决问题。但是由于更新 feed 流的操作需要依赖下游服务(这里暂且叫 A,后续文中提到下游服务均可称 A 服务),而下游的服务 A-Server 本身是个 DB 强绑定的关系,也就说明了下游的服务瓶颈在于 DB 的 QPS,这也导致了即便我本身的服务多机部署,分片处理,下游服务的短板导致不可行。而针对方案二通过大数据平台完成的话,也就是需要推荐大数据的部门协助处理,显然这个是需要排期处理,而时间上也是不可预估。
既然如此,那就借用,朱光潜老先生的一篇文章《朝抵抗力最大的路径走》。我本人相信通过合理的资源调度以及更低的成本可以克服眼前的困难,实现最终的需求效果。当然优化过程中并不是一帆风顺,当然经过两周左右的优化迭代,也终于实现了。
业务主要流程流程
整个 flush 的业务流程大致如下:
-
读取 DB 获取目前所有的 feed 类别(约 2-3w 的数据);
-
通过类别读取 Cache 每一个类别下的 feed 流元素的索引(约 1000-10w 的数据);
-
通过每一个信息的索引查询 feed 流所对应的基础数据信息(需要查约 3-4 张表);
-
计算每一个 feed 元数据的得分信息(1000w 的数据量),过程中需要淘汰一部分,调用服务 A-Server 删除当前的索引;
-
根据权重计算每一个 feed 的元素的信息,调用下游服务 A-Server,update 索引分值。
主要业务流程图具体如下:
针对上述的业务逻辑,设计出了最初方案
-
查询 DB 或者本地缓存获取索引 feed 流中的现有全集类别;
-
foreach 类别集合 Collection,查询目前所以的类别下的 feed 数据流集合并存储到 Map 中,其中 key 是类别,value 是类别对应的数组集合(key:category,value:colletion);
-
foreach 上述获取的 Map 并发起 goruntine 查询每一条信息流元素对应的基本信息,并通过粗排来淘汰需要淘汰的元素(考虑到下游的并发和 DB 的负载问题,每查询一批,sleep 一段时间),把最终符合要求的元素存储到 map 等待后续更新得分,并刷入缓存和 DB;
<