原创|ES广告倒排索引架构演进与优化

2,081 阅读6分钟

回顾

之前分享了一篇文章,介绍我们的ES广告倒排索引的架构与优化,我就不介绍了,建议先去看下这篇文章,再回来看这篇,下面只放下之前的架构图

ES倒排索引

演进

采用 canal 监听 binlog 变更

原有架构是在代码中写 MQ 消息,然后 indexbuilder 消费消息,写入到两个索引中。但这种方式有个不足是不能覆盖所有的订单或创意变更,所以倒排索引中的数据有的时候和 DB 中是不一致的。同时代码维护起来也比较麻烦。后面我们就引入了阿里开源的框架 canal ,它可以监听 MySQL 的 binlog 的变更,然后把日志发到 Kafka 中,file这样我们只需要在 indexbuilder 这个工程中消费 Kafka 的消息就行了,省去了在 dsp_adinfo 中发消息。而且 binlog 的变更可以覆盖所有的变更操作。

项目由物理机迁移到云平台

之前 index_builder 部署在物理机上,且 builder 采用主备部署,通过争抢用 zookeeper 实现的分布式锁来决定谁是主,迁移到云平台后,就去掉了这种对主备部署的方式,因为云平台有自动修复的策略。

注意

我们部署的两个 builder 一个为 m 索引,一个为 f 索引,通过环境变量 dspindexname 区分是 m 索引 还是 f 索引。同时因为这两个 builder 都要消费 Kafka 的消息,但我们知道 Kafka 处于同一个消费组的消费者只有一个能消费消息,所以要把两个 builder 放到不同的消费组中,即设置不同的 group_id,同样也是通过环境变量区分

增加检查数据一致性的定时任务

增加了检查 DB 和 ES倒排索引中的数据一致性的定时任务,每 10 分钟执行一次,如果发现不一致会发短信通知,正常情况下数据都是一致的

通过上面的一点点演进,整体架构如下所示

调整前架构图

一次线上问题

因为我们只部署了一套 ES 集群,两个索引都在上面,只是通过别名访问,完成索引的切换,这就存在单点问题,一旦集群出现问题,后果不堪设想。

就像墨菲定律所言

如果事情有变坏的可能,不管这种可能有多小,它总会发生

我们的场景是读多写少,且索引占用内存比较小,所以设置的主分片是 1 ,副本分片是 节点数-1,这样就可以保证每个节点都保存所有数据,可以减少在路由分片或节点的网络消耗。

但在一次上线过程中,忘记修改副本数了,所以副本数默认是 1,然后切换到该索引后,短时间(大约几十秒)内就导致 ES 集群瘫痪,节点内存爆满且不响应任何请求,因为主备索引都在一个 ES 集群中,所以想切回主索引也切不回去了,导致我们一段时间检索不出来广告,从而影响出价。最后没有办法只能在入口处把流量暂停,然后重启 ES,很尴尬的是,我们没有准备批量重启 ES 集群的脚本,只能挨个节点登录重启,也浪费了一些时间。再 ES 集群重启完后,重新构建索引并正确设置副本数,再打开流量,才恢复了正常,当时真的心惊肉跳,至今记忆犹新。

为何副本是 1 最后导致了整个集群瘫痪?

我们一个 ES 集群部署了 35 个节点,设置的主分片是 1,副本分片是节点数-1,即 34,这样做的好处是每个节点都有完整的数据,当请求到该节点后,直接查询数据就可以返回了,省去了路由到其他节点带来的网络消耗

而忘记修改副本数,即默认的是 1,这样出现的情况是,整个 ES 集群 35 个节点,只有 2 个节点有数据,其他节点是没有数据的,但是每个节点都是均匀的接收请求,但是这些没有数据的节点会把请求转发到有数据的两个节点,也就是这两个节点要承担其他没有数据的 33 个节点的请求压力,所以最后撑爆了整个集群

反思

这次线上问题后,我们做了反思,也需要采用一些对策来避免类似的情况发生

1. 主备索引部署在同一个 ES 集群上,存在单点问题,所以需要再部署一个 ES 集群,实现物理隔离

2. 提供重启整个 ES 集群的脚本,以备在出现问题时可以快速重启集群

3. 通过别名方式切换索引是 100% 流量切换,也容易出现问题,所以需要一个灰度慢慢切换的方式

4. 切换索引时要增加必要的检查项

5. 调用 ES 增加熔断机制,当 ES 集群出现故障时触发熔断,保护 ES 集群和服务

架构调整

首先就是重新部署了一套 ES 集群,实现物理隔离,避免一个 ES 集群出问题,影响到另一个集群

其次在作业平台中增加重启 ES 的脚本

然后就是通过引入 Nacos 配置中心,配置流量分配比例,从而实现灰度切换流量。同时支持配置变更通知

调整后的架构如下所示

调整后的架构

这个方案上线后,先采用了主备两套 ES 集群流量比为 1:1,然后观察效果,发现查询 ES 的 t99 由之前的 10ms 下降到 5ms,下降大约 50%,在 Prometheus 中埋点展示如下

ES双集群上线后效果

mapping 优化

我们对 ES 的读取还是有待优化的地方,在 kibana 中通过观察 Search Profiler 发现 build_scorer 占用了大部分时间,接近 80%,如下所示

Search Profiler

通过查询资料,发现可以把 mapping 文件中为 Integer 类型的映射字段改为 keyword 后可以提高性能,因为这些字段只有有限个值,并且查询时是通过 terms 精确匹配,所以定义为 keyword 后性能高些,至于性能高的根本原因还是需要深入源码分析的,需要抽时间深入研究。

然后我们就改变 mapping 文件开始验证,上线后的效果对比图如下所示

tp90对比

tp99对比

可见上线后,效果还是比较明显的

总结

因为对 ES 底层不够了解,也只能通过问题驱动来一点点的优化,后面会深入学习下 ES,再找可以优化的点

学无止境,如果文中哪里有问题,欢迎大佬批评指正,同时如果你有对 ES 优化有经验,也欢迎分享给我

参考资料

https://www.jianshu.com/p/9830413f62eb

https://elasticsearch.cn/article/446

https://elasticsearch.cn/question/3253

https://www.elastic.co/cn/blog/searching-numb3rs-in-5.0

https://www.elastic.co/cn/blog/better-query-planning-for-range-queries-in-elasticsearch

欢迎关注公众号 【每天晒白牙】,获取最新文章,我们一起交流,共同进步!