09&10-线上服务和存储模块

前言

1. 09 | 线上服务:如何在线上提供高并发的推荐服务

  • 在一个实际的工业级推荐系统中,训练和实现推荐模型的工作量往往连一半都没有。大量的工作都发生在搭建并维护推荐服务器、模型服务模块,以及特征和模型参数数据库等线上服务部分。同时,由于线上服务模块是直接服务用户,产生推荐结果的模块,如果一旦发生延迟增加甚至服务宕机的情况,就会产生公司级别的事故。因此毫不夸张地说,线上服务实际上是推荐系统中最关键的一个模块。线上服务如果写得不好,不仅杂乱无章,而且难以升级维护。

1.1 工业级推荐服务器的功能

  • 一个工业级的推荐服务器内部究竟做了哪些事情?图 1 中红色的部分就是线上服务模块。

  • 线上服务模块的功能非常繁杂,它不仅需要跟离线训练好的模型打交道,把离线模型进行上线,在线进行模型服务(Model Serving),还需要跟数据库打交道,把候选物品和离线处理好的特征载入到服务器
  • 而且线上服务器内部的逻辑也十分地复杂,不仅包括了一些经典的过程,比如召回层和排序层,还包括一些业务逻辑,比如照顾推荐结果多样性,流行度的一些硬性的混合规则,甚至还包括了一些 AB 测试相关的测试代码。

1.2 高并发推荐服务的整体架构

  • 宏观来讲,高并发推荐服务的整体架构主要由三个重要机制支撑,它们分别是负载均衡、缓存、推荐服务降级机制。

  • 首先是负载均衡。它是整个推荐服务能够实现高可用、可扩展的基础。当推荐服务支持的业务量达到一定规模的时候,单独依靠一台服务器是不可行的,无论这台服务器的性能有多强大,都不可能独立支撑起高 QPS(Queries Per Second,每秒查询次数)的需求。这时候,我们就需要增加服务器来分担独立节点的压力。既然有多个劳动力在干活,那我们还需要一个“工头”来分配任务,以达到按能力分配和高效率分配的目的,这个“工头”就是所谓的“负载均衡服务器”

  • 下图就很好地展示了负载均衡的原理。我们可以看到,负载均衡服务器(Load Balancer)处在一个非常重要的位置。因此在实际工程中,负载均衡服务器也经常采用非常高效的 nginx 技术选型,甚至采用专门的硬件级负载均衡设备作为解决方案。

  • 其次是合适的缓存策略
  • “负载均衡”解决高并发的思路是“增加劳动力”,那我们能否从“减少劳动量”的角度来解决高并发带来的负载压力呢?这是一个非常好的角度。要知道,推荐过程特别是基于深度学习的推荐过程往往是比较复杂的,进一步来说,当候选物品规模比较大的时候,产生推荐列表的过程其实非常消耗计算资源,服务器的“劳动量”非常大。这个时候,我们可以通过减少“硬算”推荐结果的次数来给推荐服务器减负,那具体怎么做呢?

  • 比如说,当同一个用户多次请求同样的推荐服务时,我们就可以在第一次请求时把 TA 的推荐结果缓存起来,在后续请求时直接返回缓存中的结果就可以了,不用再通过复杂的推荐逻辑重新算一遍。再比如说,对于新用户来说,因为他们几乎没有行为历史的记录,所以我们可以先按照一些规则预先缓存好几类新用户的推荐列表,等遇到新用户的时候就直接返回。因此,在一个成熟的工业级推荐系统中,合理的缓存策略甚至能够阻挡掉 90% 以上的推荐请求,大大减小推荐服务器的计算压力。

  • 最后是服务降级。

  • 但不管再强大的服务集群,再有效的缓存方案,也都有可能遭遇特殊时刻的流量洪峰或者软硬件故障。在这种特殊情况下,为了防止推荐服务彻底熔断崩溃,甚至造成相关微服务依次崩溃的“雪崩效应”,我们就要在第一时间将问题控制在推荐服务内部,而应对的最好机制就是“服务降级”。所谓“服务降级”就是抛弃原本的复杂逻辑,采用最保险、最简单、最不消耗资源的降级服务来渡过特殊时期
  • 比如对于推荐服务来说,我们可以抛弃原本的复杂推荐模型,采用基于规则的推荐方法来生成推荐列表,甚至直接在缓存或者内存中提前准备好应对故障时的默认推荐列表,做到“0”计算产出服务结果,这些都是服务降级的可行策略。

  • 总之,“负载均衡”提升服务能力,“缓存”降低服务压力,“服务降级”机制保证故障时刻的服务不崩溃,压力不传导,这三点可以看成是一个成熟稳定的高并发推荐服务的基石。

2. 10 | 存储模块:解决推荐系统特征的存储问题

  • 在推荐系统这个大饭馆中,特征工程就是负责配料和食材的厨师,那我们上堂课搭建的推荐服务器就是准备做菜的大厨。配料和食材准备好了,做菜的大厨也已经开火热锅了,这时候我们得把食材及时传到大厨那啊。这个传菜的过程就是推荐系统特征的存储和获取过程
  • 可是我们知道,类似 Embedding 这样的特征是在离线环境下生成的,而推荐服务器是在线上环境中运行的,那这些离线的特征数据是如何导入到线上让推荐服务器使用的呢
  • 以 Netflix 的推荐系统架构为例,来讲一讲存储模块在整个系统中的位置,再详细来讲推荐系统存储方案的设计原则

2.1 推荐系统存储模块的设计原则

  • Netflix 采用了非常经典的 Offline、Nearline、Online 三层推荐系统架构。架构图中最核心的位置就是我在图中用红框标出的部分,它们是三个数据库 Cassandra、MySQL 和 EVcache,这三个数据库就是 Netflix 解决特征和模型参数存储问题的钥匙

  • 对于推荐服务器来说,由于线上的 QPS 压力巨大,每次有推荐请求到来,推荐服务器都需要把相关的特征取出。这就要求推荐服务器一定要“快”。不仅如此,对于一个成熟的互联网应用来说,它的用户数和物品数一定是巨大的,几千万上亿的规模是十分常见的。所以对于存储模块来说,这么多用户和物品特征所需的存储量会特别大。这个时候,事情就很难办了,又要存储量大,又要查询快,还要面对高 QPS 的压力。很不幸,没有一个独立的数据库能经济又高效地单独完成这样复杂的任务

  • 因此,几乎所有的工业级推荐系统都会做一件事情,就是把特征的存储做成分级存储,把越频繁访问的数据放到越快的数据库甚至缓存中,把海量的全量数据放到便宜但是查询速度较慢的数据库中。

  • 举个不恰当的例子,如果你把特征数据放到基于 HDFS 的 HBase 中,虽然你可以轻松放下所有的特征数据,但要让你的推荐服务器直接访问 HBase 进行特征查询,等到查询完成,这边用户的请求早就超时中断了,而 Netflix 的三个数据库正好满足了这样分级存储的需求。

  • 比如说,Netflix 使用的 Cassandra,它作为流行的 NoSQL 数据库,具备大数据存储的能力,但为支持推荐服务器高 QPS 的需求,我们还需要把最常用的特征和模型参数存入 EVcache 这类内存数据库
  • 对于更常用的数据,我们可以把它们存储在 Guava Cache 等服务器内部缓存,甚至是服务器的内存中。总之,对于一个工程师来说,我们经常需要做出技术上的权衡,达成一个在花销和效果上平衡最优的技术方案。
  • 对于 MySQL 来说,由于它是一个强一致性的关系型数据库,一般存储的是比较关键的要求强一致性的信息,比如物品是否可以被推荐这种控制类的信息,物品分类的层级关系,用户的注册信息等等。这类信息一般是由推荐服务器进行阶段性的拉取,或者利用分级缓存进行阶段性的更新,避免因为过于频繁的访问压垮 MySQL。
  • 总的来说,推荐系统存储模块的设计原则就是“分级存储,把越频繁访问的数据放到越快的数据库甚至缓存中,把海量的全量数据放到廉价但是查询速度较慢的数据库中”
-------------The End-------------
谢谢大锅请我喝杯阔乐~