一、背景

在视频举荐场景中,一方面咱们须要让新启用的视频尽可能快的触达用户,这一点对于新闻类的内容尤为要害;另一方面咱们须要疾速辨认新物品的好坏,通过散发的流量,以及对应的后验数据,来判断新物品是否值得持续散发流量。

而这两点对于索引先验数据和后验数据的提早都有很高的要求。下文将为大家介绍看点视频举荐的索引构建计划,心愿和大家一起交换。文章作者:纪文忠,腾讯QQ端举荐研发工程师。

注:这里咱们把视频创立时就带有的数据称为先验数据,如tag,作者账号id等,而把用户行为反馈的数据称为后验数据,如曝光、点击、播放等。

二、看点视频举荐整体架构

从数据链路来看此架构图,从下往上来看,首先视频内容由内容核心通过音讯队列给到咱们,通过肯定的解决入库、建索引、生成正排/倒排数据,这时候在存储层可召回的内容约有1千万条。

而后通过召回层,通过用户画像、点击历史等特色召回出数千条视频,给到粗排层;粗排将这数千条视频打分,取数百条给到精排层;精排再一次打分,给到重排;重排依据肯定规定和策略进行打散和干涉,最终取10+条给到用户;

视频在用户侧曝光后,从上之下,是另一条数据链路:用户对视频的行为,如曝光、点击、播放、点赞、评论等通过上报至日志服务,而后通过实时/离线解决产生特色回到存储层,由此造成一个循环。

基于此架构,咱们须要设计一套召回/倒排索引,可能以实时/近实时的提早来解决所有数据。

三、方案设计

在旧计划中,索引是每半小时定时构建的,无奈满足近实时的要求。在剖析这个索引构建的计划时,咱们遇到的次要挑战有:

  • 数据虽不要求强一致性,但须要保障最终一致性;
  • 后验数据写入量极大,看点用户行为每日达到百亿+;
  • 召回零碎要求高并发、低提早、高可用。

1. 业界支流计划调研

通过比照业界支流计划,咱们能够看到,基于Redis的计划灵活性较差,间接应用比拟艰难,须要进行较多定制化开发,能够首先排除。

因而咱们可抉择的计划次要在自研或者抉择开源成熟计划。通过钻研,咱们发现如果自研索引开发成本较高,而简略的自研计划可能无奈满足业务需要,欠缺的自研索引计划所须要的开发成本往往较高,往往须要多人的团队来开发保护,最终咱们抉择了基于ES的索引服务。

至于为什么抉择基于ES,而不是抉择基于Solr,次要是因为ES有更成熟的社区,以及有腾讯云PaaS服务反对,应用起来更加灵便不便。

2. 数据链路图

(1)计划介绍

如下图所示:

这个计划从数据链路上分为两大块。

第一块,先验数据链路,就是上半局部,咱们的数据源次要来自内容核心,通过解析服务写入到CDB中。其中这个链路又分为全量链路和增量链路。

全量链路次要是在重建索引时才须要的,触发次数少但也重要。它从DB这里dump数据,写入kafka,而后通过写入服务写入ES。

增量链路是确保其实时性的链路,通过监听binlog,发送音讯至kafka,写入服务生产kafka而后写入ES。

第二块,是后验数据链路。看点的用户行为流水每天有上百亿,这个量级间接打入ES是相对扛不住的。所以咱们须要对此进行聚合计算。

这里应用Flink做了1分钟滚动窗口的聚合,而后把后果输入到写模块,失去1分钟增量的后验数据。在这里,Redis存储近7天的后验数据,写模块生产到增量数据后,须要读出当天的数据,并于增量数据累加后写回Redis,并发送对应的rowkey和后验数据音讯给到Kafka,再经由ES写入服务生产、写入ES索引。

(2)一致性问题剖析

这个数据链路存在3个一致性问题须要小心解决:

第一,Redis写模块这里,须要先读出数据,累加之后再写入。先读后写,须要保障原子性,而这里可能存在同时有其余线程在同一时间写入,造成数据不统一。

解决方案1是通过redis加锁来实现;解决方案2如下图所示,在kafka队列中,应用rowkey作为分区key,确保同一rowkey调配至同一分区,而同一只能由同一消费者生产,也就是同一rowkey由一个过程解决,再接着以rowkey作为分线程key,应用hash算法分线程,这样同一rowkey就在同一线程内解决,因而解决了此处的一致性问题。另外,通过这种计划,同一流内的一致性问题都能够解决。

第二,还是Redis写模块这里,咱们晓得Redis写入是须要先生产kafka的音讯的,那么这里就要求kafka音讯commit和redis写入须要在一个事务内实现,或者说须要保障原子性。

如果这里先commit再进行redis写入,那么如果零碎在commit完且写入redis前宕机了,那么这条音讯将失落掉;如果先写入,在commit,那么这里就可能会反复生产。

如何解决这个一致性问题呢?咱们通过先写入redis,并且写入的信息里带上工夫戳作为版本号,而后再commit音讯;写入前会比拟音讯版本号和redis的版本号,若小于,则间接抛弃;这样这个问题也解决了。

第三,咱们察看到写入ES有3个独立的过程写入,不同流写入同一个索引也会引入一致性问题。这里咱们能够剖析出,次要是先验数据的写入可能会存在一致性问题,因为后验数据写入的是不同字段,而且只有update操作,不会删除或者插入。

举一个例子,上游的MySQL这里删除一条数据,全量链路和增量链路同时执行,而刚好全量Dump时刚好取到这条数据,随后binlog写入delete记录,那么ES写入模块别离会生产到插入和写入两条音讯,而他本人无奈辨别先后顺序,最终可能导致先删除后插入,而DB里这条音讯是已删除的,这就造成了不统一。

那么这里如何解决该问题呢?其实剖析到问题之后就比拟好办,罕用的方法就是利用Kfaka的回溯能力:在Dump全量数据前记录下以后工夫戳t1,Dump实现之后,将增量链路回溯至t1即可。而这段可能不统一的工夫窗口,也就是1分钟左右,业务上是齐全能够忍耐的。

线上0停机高可用的在线索引降级流程如下图所示:

(3)写入平滑

因为Flink聚合后的数据有很大的毛刺,导入写入ES时服务不稳固,cpu和rt都有较大毛刺,写入状况如下图所示:

此处监控距离是10秒,能够看到,因为聚合窗口是1min,每分钟前10秒写入达到峰值,前面逐步缩小,而后新的一分钟开始时又周期性反复这种状况。

对此咱们须要钻研出适合的平滑写入计划,这里间接应用固定阈值来平滑写入不适合,因为业务不同工夫写入量不同,无奈给出固定阈值。

最终咱们应用以下计划来平滑写入:

咱们应用自适应的限流器来平滑写,通过统计前1分钟接管的音讯总量,来计算以后每秒可发送的音讯总量。具体实现如下图所示,将该模块拆分为读线程和写线程,读线程统计接管音讯数,并把音讯存入队列;令牌桶数据每秒更新;写线程获取令牌桶,获取不到则期待,获取到了就写入。最终咱们平滑写入后的成果如图所示:

在不同时间段,均能达到平滑的成果。

四、召回性能调优

1. 高并发场景优化

因为存在多路召回,所以召回零碎有读放大的问题,咱们ES相干的召回,总qps是50W。这么大的申请量如果间接打入ES,肯定是扛不住的,那么如何来进行优化呢?

因为大量申请的参数是雷同的,并且存在大量的热门key,因而咱们引入了多级缓存来进步召回的吞吐量和延迟时间。

咱们多级缓存计划如下图所示:

这个计划架构清晰,简单明了,整个链路: 本地缓存(BigCache)<->分布式缓存(Redis)<->ES。

通过计算,整体缓存命中率为95+%,其中本地缓存命中率75+%,分布式缓存命中率20%,打入ES的申请量大概为5%。这就大大提高了召回的吞吐量并升高了RT。

该计划还思考缓了存穿透和雪崩的问题,在线上上线之后,不久就产生了一次雪崩,ES全副申请失败,并且缓存全副未命中。起初咱们还在剖析,到底是缓存生效导致ES失败,还是ES失败导致设置申请生效,实际上这就是经典的缓存雪崩的问题。

咱们剖析一下,这个计划解决了4点问题:

  • 本地缓存定时dump到磁盘中,服务重启时将磁盘中的缓存文件加载至本地缓存。
  • 奇妙设计缓存Value,蕴含申请后果和过期工夫,由业务自行判断是否过期;当上游申请失败时,间接缩短过期工夫,并将老后果返回上游。
  • 热点key生效后,申请上游资源前进行加锁,限度单key并发申请量,爱护上游不会被霎时流量打崩。
  • 最初应用限流器兜底,如果零碎整体超时或者失败率减少,会触发限流器限度总申请量。

2. ES性能调优

(1)设置正当的primary_shards

primary_shards即主分片数,是ES索引拆分的分片数,对应底层Lucene的索引数。这个值越大,单申请的并发度就越高,但给到下层MergeResult的数量也会减少,因而这个数字不是越大越好。

依据咱们的教训联合官网倡议,通常单个shard为1~50G比拟正当,因为整个索引大小10G,咱们计算出正当取值范畴为1~10个,接下里咱们通过压测来取最合适业务的值。压测后果如下图所示:

依据压测数据,咱们抉择6作为主分片数,此时es的均匀rt13ms,99分位的rt为39ms。

(2)申请后果过滤不须要的字段

ES返回后果都是json,而且默认会带上source和_id,_version等字段,咱们把不必要的正排字段过滤掉,再应用filter_path把其余不须要的字段过滤掉,这样总共能缩小80%的包大小,过滤后果如下图所示:

包大小由26k减小到5k,带来的收益是晋升了30%的吞吐性能和升高3ms左右的rt。

(3)设置正当routing字段

ES反对应用routing字段来对索引进行路由,即在建设索引时,能够将制订字段作为路由根据,通过哈希算法间接算出其对应的分片地位。

这样查问时也能够依据指定字段来路由,到指定的分片查问而不须要到所有分片查问。依据业务特点,咱们将作者账号id puin 作为路由字段,路由过程如下图所示:

这样一来,咱们对带有作者账号id的召回的查问吞吐量能够进步6倍,整体来看,给ES带来了30%的吞吐性能晋升。

(4)敞开不须要索引或排序的字段

通过索引模板,咱们将能够将不须要索引的字段指定为"index":false,将不须要排序的字段指定为"doc_values":false。这里经测试,给ES整体带来了10%左右的吞吐性能晋升。

五、结语

本文介绍了看点视频举荐索引的构建计划,服务于看点视频的CB类型召回。其特点是,开发成本低,应用灵便不便,功能丰富,性能较高,合乎线上要求。

上线以来服务于关注召回、冷启动召回、tag画像召回、账号画像召回等许多路召回,为看点视频带来较大业务增长。将来随着业务进一步增长,咱们会进一步优化该计划,目前来看,该技术计划还当先于业务一段时间。最初欢送各位同学交换,欢送在评论区留言。