竞价服务的预算控制模块

2015-01-08

广告实时竞价这边,出价是要涉及到钱的。客户在我们这边建一个广告投放活动。这个活动有预算,如果预算用完了,那么这个活动就不应该继续参与竞价了。预算控制是竞价服务其中一个模块。

那么现在存在什么问题呢?根本控制不住!预算会超!这让我们很不好跟客户交待。

为什么会超呢?活动预算相关的数据是在另一个数据库中,从数据库到竞价服务,数据是有延迟的。竞价服务这边并不知道某活动的预算已经用完了,继续出价。由于竞价请求的QPS很高,稍微一点点的延迟,就超预算了。

这个问题还要从架构演化谈起。

据说起初(我还没来这里之前)竞价服务是每次去活动数据库中取数据。这样数据是一致的,但是竞价服务对响应时间要求非常高,取一条数据要去访问数据库一次数据库根本没法玩,即使是redis也不行。

活动这一部分的数据量并不大,所以后来就在竞价进程内部做了个活动数据库,然后做一个dispatch服务负责周期性地从数据库同步到竞价进程内,相当于是做一个本地缓存。

预算数值变化的流程是这样的:

  • 由dispatch将活动数据同步到竞价进程中
  • 预算没超,竞价进程让活动参与竞价(读)
  • 获胜通知的进程与数据库交互,修改预算(写)
  • 如此反复

这里有几个点需要注意。一个是数据流动是异步的,修改预算并不是由竞价进程执行,竞得后的处理是其它进程。另一个是竞价进程是分布的,有许许多多进程。我们要尽量的让竞价进程是"无状态"的,它是一个计算型服务,性能方面要求很高。前面有ngnix做负载均衡,把竞价请求转发到各个竞价进程中。

这个流程,数据延迟不可避免,于是预算控制就控不住了。

说到底,这是一个性能跟一致性的矛盾,为了竞价进程的性能,必须本地缓存数据。本地缓存数据之后,每个竞价进程不知道其它竞价进程对数据的修改。另外,反馈也是异步的过程,最终竞价进程拿到的都是非真实的预算在做决策。

说一说解决,只是提下思路。

如果是从业务层面想办法,可以用一个概率算法,在快要接近预算耗尽时,就降低此活动出价的概率,这样子速度慢下来,就可以缓解预算不会超得太多。

架构层想办法,现在的同步是由dispatch周期性地拉。如果能改成一个主动推,实时性就会提高。获胜通知那边,如果发现预算已经不多了,除了写数据库,同时还向竞价服务推更新消息。