本文共 4601 字,大约阅读时间需要 15 分钟。
本文首发于InfoQ
作者:Anton Hägerstrand
翻译:杨振涛
目录
1.数据量
2.版本
3.节点配置
4.索引结构
5.性能
Meltwater 每天要处理数百万量级的帖子数据,因此需要一种能处理该量级数据的存储和检索技术。
从0.11.X 版本开始我们就已经是Elasticsearch的忠实用户了。在经历了一些波折之后,最终我们认为做出了正确的技术选型。
Elasticsearch 用于支持我们的主要媒体监控应用,客户通过该应用可以检索和分析媒体数据,比如新闻文章、(公开的)Facebook 帖子、Instagram 帖子、博客和微博。我们通过使用一个混合API来收集这些内容,并爬取和稍作加工,使得它们可被Elasticsearch检索到。
本文将分享我们所学到的经验、如何调优 Elasticsearch,以及要绕过的一些陷阱。
如果想了解更多关于我们在 Elasticsearch 方面的点滴,可参考之前博文中的 和 。
每天都有数量相当庞大的新闻和微博产生;在高峰期需要索引大约300多万社论文章,和近1亿条社交帖子数据。其中社论数据长期保存以供检索(可回溯到2009年),社交帖子数据保存近15个月的。当前的主分片数据使用了大约200 TB的磁盘空间,副本数据大约600 TB。
我们的业务每分钟有3千次请求。所有的请求通过一个叫做 “search-service” 的服务,该服务会依次完成所有与 Elasticsearch 集群的交互。大部分检索规则比较复杂,包括在面板和新闻流中。比如,一个客户可能对 Tesla 和 Elon Musk 感兴趣,但希望排除所有关于 SpaceX 或 PayPal 的信息。用户可以使用一种与Lucene查询语法类似的灵活语法,如下:
Tesla AND "Elon Musk" NOT (SpaceX OR PayPal)
我们最长的此类查询有60多页。重点是:除了每分钟3千次请求以外,没有一个查询是像在 Google 里查询 “Barack Obama” 这么简单的;这简直就是可怕的野兽,但ES节点必须努力找出一个匹配的文档集。
我们运行的是一个基于 Elasticsearch 1.7.6 的定制版本。该版本与1.7.6 主干版本的唯一区别是,我们向后移植(backport)了 作为缓存。该功能是从Lucene 5移植到Lucene 4的,对应移植到了ES 1.X版本。Elasticsearch 1.X中使用默认的bitset作为缓存,对于稀疏结果来说开销非常大,不过在Elasticsearch 2.X中已经做了优化。
为何不使用较新版本的Elasticsearch呢?主要原因是升级困难。在主版本间滚动升级只适用于从ES 5到6(从ES 2到5应该也支持滚动升级,但没有试过)。因此,我们只能通过重启整个集群来升级。宕机对我们来说几乎不可接受,但或许可以应对一次重启所带来的大约30-60分钟宕机时间;而真正令人担心的,是一旦发生故障并没有真正的回滚过程。
截止目前我们选择了不升级集群。当然我们希望可以升级,但目前有更为紧迫的任务。实际上该如何实施升级尚未有定论,很可能选择创建另一个新的集群,而不是升级现有的。
我们自2017年6月开始在AWS上运行主集群,使用 i3.2xlarge 实例作为数据节点。之前我们在 COLO(Co-located Data Center)里运行集群,但后续迁移到了AWS云,以便在新机器宕机时能赢得时间,使得我们在扩容和缩容时更加弹性。
我们在不同的可用区运行3个候选master节点,并设置 discovery.zen.minimum_master_nodes 为2。这是避免脑裂问题 非常通用的策略。
我们的数据集在存储方面,要求80%容量和3个以上的副本,这使得我们运行了430个数据节点。起初打算使用不同层级的数据,在较慢的磁盘上存储较旧的数据,但是由于我们只有相关的较低量级旧于15个月的数据(只有编辑数据,因为我们丢弃了旧的社交数据),然而这并未奏效。每个月的硬件开销远大于运行在COLO中,但是云服务支持扩容集群到2倍,而几乎不用花费多少时间。
你可能会问,为何选择自己管理维护ES集群。其实我们考虑过托管方案,但最后还是选择自己安装,理由是: 暴露给用户的可控性太差了, 的成本比直接在EC2上运行集群要高2-3倍。
为了在某个可用区宕机时保护我们自身,节点分散于 eu-west-1 的所有3个可用区。我们使用 来完成该项配置。它提供了一个叫做aws_availability_zone的节点属性,我们把cluster.routing.allocation.awareness.attributes 设置为aws_availability_zone。这保证了ES的副本尽可能地存储在不同的可用区,而查询尽可能被路由到相同可用区的节点。
这些实例运行的是 Amazon Linux,临时挂载为ext4,有约64GB的内存。我们分配了26GB用于ES节点的堆内存,剩下的用于磁盘缓存。为何是26GB?因为 JVM 是在一个黑魔法之上构建的()。我们同时使用 自动扩容组来提供实例,并使用 完成一切安装配置。
因为我们的数据和查询都是基于时间序列的,所以使用了 ,类似于ELK (elasticsearch, logstash, kibana) 。同时也让不同类型的数据保存在不同的索引库中,以便诸如社论文档和社交文档类数据最终位于不同的每日索引库中。这样可以在需要的时候只丢弃社交索引,并增加一些查询优化。每个日索引运行在两个分片中的一个。
该项设置产生了大量的分片(接近40k)。有了这么多的分片和节点,集群操作有时变得更特殊。比如,删除索引似乎成为集群master的能力瓶颈,它需要把集群状态信息推送给所有节点。我们的集群状态数据约100 MB,但通过TCP压缩可减少到3 MB
(可以通过 curl localhost:9200/_cluster/state/_all 查看你自己集群的状态数据)。Master节点仍然需要在每次变更时推送1.3 GB数据(430 节点 x 3 MB 状态大小)。除了这1.3 GB数据外,还有约860 MB必须在可用区(比如 最基本的通过公共互联网)之间传输。这会比较耗时,尤其是在删除数百个索引时。我们希望新版本的Elasticsearch能优化这一点,首先从ES 2.0支持仅发送 这一特性开始。
如前所述,我们的ES集群为了满足客户的检索需求,需要处理一些非常复杂的查询。
为应对查询负载,过去几年我们在性能方面做了大量的工作。我们必须尝试公平分享ES集群的性能测试,从下列引文就可以看出。
不幸的是,当集群宕机的时候,不到三分之一的查询能成功完成。我们相信测试本身导致了集群宕机。 —— 摘录自使用真实查询在新ES集群平台上的第一次性能测试
为了控制查询执行过程,我们开发了一个插件,实现了一系列自定义查询类型。通过使用这些查询类型来提供 Elasticsearch 官方版本不支持的功能和性能优化。比如,我们实现了 phrases 中的 wildcard 查询,支持在 SpanNear 查询中执行;另一个优化是支持“*”代替 match-all-query ;还有其他一系列特性。
Elasticsearch 和 Lucene 的性能高度依赖于具体的查询和数据,没有银弹。即便如此,仍可给出一些从基础到进阶的参考:
图表说明:响应时间。有/没有 重写Lucene查询执行。同时也表明不再有节点每天多次发生内存不足。
顺便说明下,因为我知道会面临一个问题:从上一次性能测试我们知道通过升级到ES 2.X能小幅提升性能,但是并不能改变什么。话虽如此,但如果你已经从ES 1.X集群迁移到了ES 2.X,我们很乐意听取关于你如何完成迁移的实践经验。
如果读到了这里,说明你对 Elasticsearch 是真爱啊(或者至少你是真的需要它)。我们很乐意学习你的经验,以及任何可以分享的内容。欢迎在评论区分享你的反馈和问题。
英文原文链接:
转载地址:http://cqtax.baihongyu.com/