Loggly日志管理服务在其很多核心功能里使用ElasticSearch作为搜索引擎。Jon Gifford在其文章“ElasticSearch vs Solr”中指出,日志管理领域对搜索技术有了更高的要求。总的来说,它必须能够:
- 可靠地进行大规模实时索引-对我们来说,每秒处理10万条以上日志数据;
- 高性能、可靠地处理同一索引上的高并发搜索请求。
当我们搭建Gen2日志管理服务时,我们对ElasticSearch的各项配置信息进行了反复研究,以便能获得索引和搜索的最高性能。不幸的是,这些配置项散落各处,一一找到它们并不容易。这篇文章总结了我们的经验,您可以参考本文列出的这些项,优化ES在您应用中的使用效果。
技巧1:在开始前,要搞清楚部署拓扑结构
Loggly 使用ES 0.90.13,采用主节点与数据节点相互分离的部署拓扑结构,这里我们不过多讲解其中的细节,但我们要强调的是在决定如何配置之前,你要对部署拓扑结构非常清晰。
此外,我们使用ES节点客户端(ES node client)与数据节点交互。这使得客户端对数据节点是透明的;它只关心与节点客户端(node client)的交互。要设置节点为主节点还是数据节点,只需要通过将两个属性设置为true或false。例如要设置一个ElasticSearch为数据节点,可以这样设置:
node.master:false 和node.data: true
很容易对吧。那么下面我们将讨论一些你可能感兴趣的ES高级属性。在大部分情况下,ES的默认配置都是够用的,但如果你想让你的服务表现不论何时都能像我们看到的高性能日志管理服务一样,那下面的建议就对你有用了。
技巧2:mlockall属性是获取性能效率回报的终极武器
Linux将它的物理内存(RAM)分成一组称为pages的内存块。而Swapping是将内存页拷贝到硬盘上一块预定义空间(swapspace,交换空间),从而释放内存的处理过程。物理内存和交换空间的合并大小是可用的虚拟内存的数量。
Swapping有个缺点。与内存相比,磁盘速度很慢。内存速度以纳秒计,而硬盘速度却以毫秒计;所以访问磁盘的时间开销是访问内存的数十万倍。访问磁盘次数越多,性能就越慢,所以要想方设法避免Swapping。
mlockall 属性可以让ES节点不进行Swapping。(注意仅适用于Linux/Unix系统)。这个属性可以在yaml文件中设置。
bootstrap.mlockall:true
mlockall 默认是false的,意味着ES节点被允许Swapping。注意,如果你在文件中设置了该属性,就必须重启ES节点。你可以通过执行以下语句查看属性设置是否生效:
curlhttp://localhost:9200/_nodes/process?pretty
如果你决定设置这个属性,一定确保通过-DXmx选项或ES_HEAP_SIZE给ES节点预留足够的内存。
技巧3: discovery.zen 属性集合控制ElasticSearch的发现协议
Zen发现协议用于ElasticSearch发现集群中的其它节点,并建立通讯。discovery.zen.*属性集合构成了zen发现协议。单播和多播均是发现协议的有效组成部分:
多播是指当发送一个或多个请求给所有节点时,集群中的节点将被发现。
2、单播是在节点和discovery.zen.ping.unicast.hosts中的IP地址之间的一对一连接。
为了使单播生效,你需要将discovery.zen.ping.multicast.enabled设置为false。还需要通过discovery.zen.ping.unicast.hosts来设置一组主机域名,其中应包含用于通信的主节点域名。
discovery.zen.minimum_master_nodes用于设置为了进行集群操作,一个节点需要能看见“see”的最小数量的合格主节点。强烈建议在集群中超过2个节点时,将该值设置为比1大的值。该值的一个计算方法是N/2+ 1,N是主节点数量。
数据节点和主节点通过以下两种不同方式相互检查:
- 主节点通过pinging集群中所有其它节点,判断它们是否正常运行;
- 所有其它节点通过pinging主节点确认它们是否正常运行,否则就需要启动选举程序。
节点检测过程由discover.zen.fd.ping_timeout属性控制。该属性定义了节点等待反馈的最长时间,默认值是30s。如果你的网速条件不好,需要适当调整该值大小。如果网速很慢,这个值应该设置更高一些。值越高,发现失败的可能性就越小。
Loggly设置discovery.zen属性集合如下:
discovery.zen.fd.ping_timeout:30s
discovery.zen.minimum_master_nodes:2
discovery.zen.ping.multicast.enabled:false
discovery.zen.ping.unicast.hosts:[“esmaster01″,”esmaster02″,”esmaster03″]
以上属性的含义是,节点检测超时为30s,通过设置discovery.zen.fd.ping_timeout即可。此外,至少两个主节点要能被其它节点检测到(我们一共3个主节点)。采用单播协议,单播域名列表是:esmaster01,esmaster02, esmaster03。
技巧4:谨慎对待delete_all_indices!
有个特别重要的事情是ES中的curl API并没有内置很好认证机制。一共简单的curlAPI就能导致索引全部被删,丢失所有数据。下面就是一个会导致误删的指令:
curl-XDELETE ‘http://localhost:9200/*/’
为了避免这样的悲剧发生,你只需要设置以下属性:
action.disable_delete_all_indices:true.
这个属性可以确保即便以上curl指令被执行,也不会删除索引导致错误。
技巧5: 字段数据缓存会导致极慢的分类搜索(facet search)
这是ElasticSearch指南中如何描述字段数据缓存的:
字段数据缓存主要用在对一个字段进行排序或分类时。它将把所有字段值加载到内存。为一个字段建立字段数据缓存将是代价高昂的,需要分配足够的内存,确保能完全加载。
你要牢记,该值设置不当将导致:
- 分类搜索和排序性能低下
- 如果你对很大的索引进行分类查询,将导致ES节点内存溢出
例如:
indices.fielddata.cache.size:25%
在设置这个值时,关键要考虑你的应用将要进行什么类型分类搜索。
技巧6: 优化索引请求
在Loggly,我们构建了自己的索引管理系统,因为日志管理的本质意味着将有频繁的更新和映射关系变更。这个索引管理系统的功能就是管理ES集群的索引。当索引需要根据现有配置策略被创建或关闭时,它会做检查。索引管理有很多策略。例如,当索引大小增长到特定值或者存在时间超过某个时间值,索引管理系统将关闭旧的,创建新的。
本文由日志帮(公众号id:rizhibang)翻译整理。
当索引管理系统发送一个索引请求给节点处理时,节点更新它自己的映射关系表并发给主节点。主节点会发送给那个节点一共更旧版本的映射关系表。如果有冲突,并不是坏事(也就是说集群实际上有正确的映射关系表),我们只需要从这个节点向主节点发送一个更新。为了索引请求更高效,我们在数据节点上设置了这个属性。
indices.cluster.send_refresh_mapping:false
而反过来,发送更新的映射关系表更重要,因为某些原因,主节点上的映射关系表与实际节点上的冲突。这种情况,更新映射关系表将会在主节点上记录一个警告。
技巧7: 教你使用ElasticSearch分配相关属性
分片(Shard)分配是分配分片给节点的处理过程。这可能发生在初始恢复、副本分配或再平衡过程中。也可能发生在添加或删除节点时。
cluster.routing.allocation.cluster_concurrent_rebalance属性指定用于并发再平衡的分片数。此属性的设置要取决于硬盘条件,如CPU数量,IO性能等。如果该属性设置不当,将影响ElasticSearch索引性能。
cluster.routing.allocation.cluster_concurrent_rebalance:2
该值默认为2,意思是任何时间点,只能有2个分片被移动。该值设置低一些,能降低分片再平衡,从而避免影响索引。
另一个分片分配属性是cluster.routing.allocation.disk.threshold_enabled。如果该属性设置为true,在给节点分配分片时将考虑磁盘空间。
当设置为true时,分片分配会考虑两种情况:低位值、高位值。
- 低位值对应的磁盘使用率, 达到后ES不再分配新分片。在下面的例子中,磁盘使用率97%时,ES将停止分配分片
- 高位值对应的磁盘使用率,达到后分片开始移出节点(下面示例中为99%)
cluster.routing.allocation.disk.threshold_enabled:true
cluster.routing.allocation.disk.watermark.low:.97
cluster.routing.allocation.disk.watermark.high:.99
技巧8:设置恢复相关属性可以缩短重启时间
ES包含几个恢复相关的属性项,使用它们可以改进ElasticSearch集群的恢复和重启时间。我们下面将展示几个简单的示例。对你来说,最佳的值取决于正在使用的硬件条件,我们能给的建议就是测试、测试,还是测试。
这个属性定义的是,在任何时间,一个节点可以有多少分片被用于执行恢复。回收分片是一个IO密集型操作,所以需要谨慎设置该属性值。
cluster.routing.allocation.node_initial_primaries_recoveries:18
这个属性控制的是单个节点上同时初始化的主要分片(primaryshards)数量。从节点传输到对等节点的回收分片的平行流数量是由indices.recovery.concurrent_streams属性控制。下面的值是给亚马逊云的实例设置的,如果你是使用了自己的硬件,这个值可能需要设置更高。max_bytes_per_sec属性用于设置每秒传输多少字节,这个属性也是需要根据硬件条件来配置的。
indices.recovery.concurrent_streams:4
indices.recovery.max_bytes_per_sec:40mb
所有上述属性,需要在重启集群后生效。
技巧9:Threadpool属性防止数据丢失
ES节点有几个threadpools属性,用于改进节点内管理的线程数量。在Loggly,我们广泛使用块请求(bulkrequest),我们发现使用threadpool.bulk.queue_size属性给批量线程池设置正确的数值至关重要,能避免数据丢失或者批量重试。
threadpool.bulk.queue_size:3000
这个属性值是关于块请求的。它指定了在没有更多线程来处理批量请求时,ES节点队列中等待处理的请求数。根据你的块请求负载情况设置该值。如果你的块请求数量比这个值高,你将收到像下方示例中的一个RemoteTransportException异常。
注意,在ES中,块请求队列中,一个分片对应一个的项,所以如果你想发送的块请求数包含很多给分片的数据项,那么这个属性的数值需要设置为比你要发送的批量请求数更高的数值。例如,单个块请求中包含了10个分片的数据项,那即使你只发送一个块请求,也必须至少将队列大小设置为10。这个值设置过大,会消耗你的JVM堆,但却可以让ES充分发挥队列功效,解放你的客户端。
你要么把这个值设置高一些,要么在客户端妥善处理好RemoteTransportException异常。如果没有妥善处理异常,你将丢失数据。下面我们将通过发送超过10个块请求(队列值设置为10)的方式模拟展示异常。
RemoteTransportException[[<Bantam>][inet[/192.168.76.1:9300]][bulk/shard]];nested: EsRejectedExecutionException[rejected execution (queue capacity 10) on org.elasticsearch.action.support.replication.TransportShardReplicationOperationAction$AsyncShardOperationAction$1@13fe9be];
总结:ES的配置属性对它的灵活伸缩性至关重要
ElasticSearch配置项对其有效性越深入,Loggly的收益也就越大,因为在我们的使用案例中,已经将ES的设计参数用到了极致(有时更甚,后续文章我们将继续分享)。如果在使用默认配置的情况下,已经能满足你应用现阶段需要,那么请放心,随着应用增长,你还有很大的优化空间。