Elasticsearch是非常灵活且功能丰富的搜索引擎,提供了开箱即用的美好体验。但是,在实战业务场景中,经常会出现远远低于预期的索引和查询速度。
下面是针对Elasticsearch索引性能进行调优的建议。
批量请求将产生比单文档索引请求好得多的性能。
为了知道批量请求的最佳大小,尝试在具有单个分片的单个节点上运行基准测试。 首先尝试索引100个文档,然后是200,然后是400,等等。 当索引速度开始稳定时,就达到了数据批量请求的最佳大小。
一般而言,宁可批量索引文档少一些,而不是在太多文档的方向上犯错。 请注意,如果批量请求文档太大,可能会使Elasticsearch集群受到内存压力,因此建议避免每个请求超出几十兆字节,即使较大的请求看起来效果更好。
发送批量请求的单个线程不太可能将Elasticsearch集群的索引容量最大化。 为了使用集群的所有资源,可以从多个线程或进程发送数据。 除了更好地利用集群的资源,这应该有助于降低每个fsync的成本。
请注意TOO_MANY_REQUESTS(429)响应代码(JAVA客户端的EsRejectedExecutionException),这是Elasticsearch提示无法跟上当前索引速率的方式。 发生这种情况时,应该再次尝试暂停索引,理想情况下使用随机策略调整索引并发量。
与调整批量请求(bulk request)大小类似,只有测试才能确定最佳的worker数量。 这可以通过逐渐增加工作者数量来测试,直到集群上的I/O或CPU饱和。
为了让索引文档对查询可见,需要执行refresh操作,该操作是有成本的。当正在进行索引时,不断地执行refresh操作,将影响索引速度。
默认的index.refresh_interval是1s,这迫使Elasticsearch每秒创建一个新的分段。 增加这个参数值(比如说30s)将提升索引速度。
PUT INDEX-NAME/_settings
{
"refresh_interval" : "30s"
}
如果需要一次加载大量数据,则应该将index.refresh_interval设置为-1,并将index.number_of_replicas设置为0,来禁用刷新(refresh)。这会暂时使索引处于危险之中,因为任何分片的丢失都将导致数据丢失,但是同时索引将会更快,因为文档只被索引一次。初始加载完成后,可以将index.refresh_interval和index.number_of_replicas设置回其原始值。
通过禁止swap交换内存,确保操作系统不会交换出Java进程。
具体禁用swapping的操作,可以参考下面链接:
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/setup-configuration-memory.html
文件系统缓存(filesystem cache)将用于缓冲I/O操作。 确保运行Elasticsearch的节点获得至少一半的主机内存,提供给filesystem cache。
索引具有显式id的文档时,Elasticsearch需要检查具有相同id的文档是否已经存在于相同的分片中,这是费时的操作,并且随着索引增长而变得更加费时。 通过使用自动生成的ID,Elasticsearch可以跳过这个检查,这使索引更快。
PUT test-index
{
"settings":{
"number_of_shards": 3,
"number_of_replicas": 1
}
}
POST test-index/_doc
{
"sku": "002",
"title": "bbb"
}
GET test-index/_search
一般而言,索引是I/O密集的,需要准备如下硬件:
(1)为filesystem cache分配更多的内存;
(2)使用SSD硬盘;
(3)使用local storage(不要使用NFS、SMB 等remote filesystem)
如果节点只做大量的索引,确保index.memory.index_buffer_size足够大,每个分片最多可以提供512 MB的索引缓冲区。超过这一设置,索引性能通常也不会提升。
Elasticsearch采用该设置(Java堆的百分比,或者绝对字节大小),并将其作为所有活动分片(active shards)的共享缓冲区。非常活跃的分片自然比执行轻量级索引的分片,更多地使用这个缓冲区。
默认设置为 10%,通常是足够的。如我们给JVM 10GM内存,索引缓冲区将为1GB,这足以两个索引很重的分片。