回顾
之前分享了一篇文章 广告倒排索引架构与优化,介绍我们的ES广告倒排索引的架构与优化,我就不介绍了,建议先去看下这篇文章,再回来看这篇,下面只放下之前的架构图
演进
采用 canal 监听 binlog 变更
原有架构是在代码中写 MQ 消息,然后 index_builder 消费消息,写入到两个索引中。但这种方式有个不足是不能覆盖所有的订单或创意变更,所以倒排索引中的数据有的时候和 DB 中是不一致的。同时代码维护起来也比较麻烦。后面我们就引入了阿里开源的框架 canal ,它可以监听 MySQL 的 binlog 的变更,然后把日志发到 Kafka 中,这样我们只需要在 index_builder 这个工程中消费 Kafka 的消息就行了,省去了在 dsp_adinfo 中发消息。而且 binlog 的变更可以覆盖所有的变更操作。
项目由物理机迁移到云平台
之前 index_builder 部署在物理机上,且 builder 采用主备部署,通过争抢用 zookeeper 实现的分布式锁来决定谁是主,迁移到云平台后,就去掉了这种对主备部署的方式,因为云平台有自动修复的策略。
注意
我们部署的两个 builder 一个为 m 索引,一个为 f 索引,通过环境变量 dsp_index_name 区分是 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 个节点的请求压力,所以最后撑爆了整个集群
反思
这次线上问题后,我们做了反思,也需要采用一些对策来避免类似的情况发生
- 主备索引部署在同一个 ES 集群上,存在单点问题,所以需要再部署一个 ES 集群,实现物理隔离
- 提供重启整个 ES 集群的脚本,以备在出现问题时可以快速重启集群
- 通过别名方式切换索引是 100% 流量切换,也容易出现问题,所以需要一个灰度慢慢切换的方式
- 切换索引时要增加必要的检查项
- 调用 ES 增加熔断机制,当 ES 集群出现故障时触发熔断,保护 ES 集群和服务
架构调整
首先就是重新部署了一套 ES 集群,实现物理隔离,避免一个 ES 集群出问题,影响到另一个集群
其次在作业平台中增加重启 ES 的脚本
然后就是通过引入 Nacos 配置中心,配置流量分配比例,从而实现灰度切换流量。同时支持配置变更通知
调整后的架构如下所示
这个方案上线后,先采用了主备两套 ES 集群流量比为 1:1,然后观察效果,发现查询 ES 的 t99 由之前的 10ms 下降到 5ms,下降大约 50%,在 Prometheus 中埋点展示如下
mapping 优化
我们对 ES 的读取还是有待优化的地方,在 kibana 中通过观察 Search Profiler 发现 build_scorer 占用了大部分时间,接近 80%,如下所示
通过查询资料,发现可以把 mapping 文件中为 Integer 类型的映射字段改为 keyword 后可以提高性能,因为这些字段只有有限个值,并且查询时是通过 terms 精确匹配,所以定义为 keyword 后性能高些,至于性能高的根本原因还是需要深入源码分析的,需要抽时间深入研究。
然后我们就改变 mapping 文件开始验证,上线后的效果对比图如下所示
可见上线后,效果还是比较明显的
总结
因为对 ES 底层不够了解,也只能通过问题驱动来一点点的优化,后面会深入学习下 ES,再找可以优化的点
学无止境,如果文中哪里有问题,欢迎大佬批评指正,同时如果你有对 ES 优化有经验,也欢迎分享给我
参考资料
https://www. jianshu.com/p/9830413f6 2eb https:// elasticsearch.cn/articl e/446 https:// elasticsearch.cn/questi on/3253 https://www. elastic.co/cn/blog/sear ching-numb3rs-in-5.0 https://www. elastic.co/cn/blog/bett er-query-planning-for-range-queries-in-elasticsearch