本章节主要为您介绍使用表格存储搭建Feed流的方案原理。

方案架构图

存储模块

如架构图所示,Feed流中的以下内容使用表格存储进行存储。
  • 个人页Timeline:每个用户的发件箱,也就是自己的个人页页面。
  • 关注页Timeline:每个用户的收件箱,也就是自己的关注页页面,内容都是自己关注人发布的消息。
  • 关注列表:保存账号关系,例如朋友圈中的好友关系、微博中的关注列表等。
  • 虚拟关注列表:主要用来个性化和广告。

发布及读取流程

  • 拉结合流程
    • 发布Feed
      1. Feed消息先进入一个队列服务。
      2. 先从关注列表中读取到自己的粉丝列表,以及判断自己是否是大V。
      3. 将自己的Feed消息写入个人页Timeline(发件箱)。如果是大V,写入流程到此结束。
      4. 如果是普通用户,还需要将自己的Feed消息写给自己的粉丝,如果有100个粉丝,那么就要写给100个用户,包括Feed内容和Feed ID。
      5. 第三步和第四步可以合并在一起,使用BatchWriteRow接口一次性将多行数据写入表格存储。
      6. 发布Feed的流程结束。
    • 读取Feed流
      1. 先读取自己关注的大V列表。
      2. 通过GetRange读取自己的收件箱。范围起始位置是上次读取到的最新Feed的ID;结束位置可以使当前时间,也可以是MAX,建议是MAX值。
      3. 如果有关注的大V,则再次并发读取每一个大V的发件箱。如果关注了10个大V,那么则需要10次访问。
      4. 合并2和3步的结果,然后按时间排序,返回给用户。

    至此,使用推拉结合方式的发布、读取Feed流的流程结束。

  • 推模式流程
    如果只是用推模式了,流程更简单。
    • 发布Feed流:不用区分是否大V,所有用户的流程都一样。
    • 读取Feed流:不需要第1步,也不需要第3步,只需要第2步即可,将之前的2 + N(N是关注的大V个数)次网络开销减少为1次网络开销。读取延时大幅降级。

个性化和定向广告

性化和定向广告是需求度很高的两个功能。个性化可以服务好用户,增大产品竞争力和用户粘性。定向广告可以为产品增加盈利渠道,而且不引起用户反感。 在Feed流里这两种功能的实现方式类似,我们以实现定向广告的流程为例来说明。

  1. 通过用户特征分析对用户分类,例如今年刚上大学的新生(新生类)。
    说明 具体的用户特征分析可以依靠表格存储 + MaxCompute。
  2. 创建一个广告账号:新生广告。
  3. 让这些具有新生特征的用户虚拟关注新生广告账号。
    说明 用户看不到这一层关注关系。
  4. 从七月份开始就通过新生广告账号发送广告。

每个用户可能会有多个特征,那么就可能虚拟关注多个广告账号。

方案优势

使用表格存储架构实现Feed流具有以下优势。
  • 只使用1种系统,架构、实现简单。不再需要访问多个系统,架构、开发、测试、运维都能节省人力时间。
  • 表格存储主键自增列功能性能极优。表格存储主键列自增不需要表锁和行锁,性能要远远高于关系型数据库。
  • 表格存储采用分布式存储,可以存储所有Feed。
  • 表格存储的存储成本低,您无须将Feed ID和内容分开存储。
  • 全托管服务,无运维操作,更无需分库分表。
  • 磁盘型数据库,成本低。
  • 可靠性10个9,数据更可靠,更不易丢失。
  • 大V和普通用户的切分阈值更高,读取大V的次数更少,整体延时更低。

缺陷及优化方法

如果使用大V/普通用户的切分方式,大V使用拉模式,普通用户使用推模式,那么这种架构就会存在一种很大的风险。例如某个大V突然发了一个很有话题性的Feed,那么就有可能导致整个Feed产品中的所有用户都没法读取新内容,以下是粉丝读取流程:
  1. 大V发送Feed消息。
  2. 大V使用拉模式。
  3. 大V的活跃粉丝(用户群A)开始通过拉模式读取大V的新Feed。
  4. Feed内容太有话题性,快速传播。
  5. 未登录的大V粉丝(用户群B)开始登录产品,登录进去后自动刷新,再次通过读3步骤读取大V的Feed内容。
  6. 非粉丝(用户群C)去大V的个人页Timeline里面去围观,再次需要读取大V个人的Timeline,同读3。

结果就是,平时正常流量只有用户群A,结果现在却是用户群A + 用户群B + 用户群C,流量增加了好几倍,甚至几十倍,导致读3路径的服务模块被打到server busy或者机器资源被打满,导致读取大V的读3路径无法返回请求,如果Feed产品中的用户都有关注大V,那么基本上所有用户都会卡死在读取大V的读3路径上,然后就没法刷新了。

所以设计时需重点关心下面两点:
  • 单个模块的不可用,不应该阻止整个关键的读Feed流路径。如果大V的无法读取,但是普通用户的要能返回,等服务恢复后,再补齐大V的内容即可。
  • 当模块无法承受这么大流量的时候,模块不应该完全不可服务,而应该能继续提供最大的服务能力,超过的拒绝掉。

优化方法

  • 方法一:不使用大V/普通用户的优化方式,而使用活跃用户/非活跃用户的优化方式,这种优化方式能把用户群A和部分用户群B分流到其他更分散的多个路径上去。而且即使读3路径不可用,仍然对活跃用户无任何影响。
  • 方法二:完全使用推模式可以彻底解决这个问题,但会增大存储量,并增长大V微博发送总时间,从发给第一个粉丝到发给最后一个粉丝可能要几分钟时间(一亿粉丝,100万行每秒,需要100秒),还需要为最大并发预留好资源(如果使用阿里云表格存储,则不需要考虑预留最大额度资源的问题)。