Elasticsearch优化——搜索速度优化
Elasticsearch优化——搜索速度优化
1. 为文件系统cache预留足够的内存
filesystem cache
越大越好,为了使得搜索速度更快,ES严重依赖filesystem cache
。一般来说,需要至少一般的可用内存作为filesystem cache
,这样ES可以在内存中保有索引的热点区域
2. 使用更快的硬件
搜索一般是I/O bound的,此时,你需要:
- 为
filesystem cache
分配更多的内存。 - 使用SSD硬盘。
- 使用local storage,不要使用NFS、SMB等远程文件系统。
3. 文档模型
文档需要使用适合的类型,从而使得搜索时操作消耗更少的资源。避免使用join操作,嵌套会使查询慢几倍,父子关系可能使查询慢数百倍。
4. 预索引数据
针对某些查询的模式在优化数据的索引方式。例如,如果所有文档都有一个price字段,并且大多数查询在一个固定范围上进行range聚合,那么可以通过将范围"pre-indexing"(预索引)到索引中,并使用terms聚合加快聚合速度。
例如,文档起初是这样的:
PUT index/type/1
{
"name":"苹果",
"price":13
}
采用下面的搜索方式:
GET index/type/_search
{
"aggs":{
"price_ranges":{
"range":{
"field":"price",
"ranges":[
{"to":10},
{"from":10,"to":50},
{"from":50,"to":100},
{"from":100,"to":150}
]
}
}
}
}
那么我们优化的是在建立索引时对文档进行富化,增加price_range字段,mapping为keyword类型:
PUT index/_mapping/type
{
"properties":{
"price_range":{
"type":"keyword"
}
}
}
PUT index/type/1
{
"name":"苹果",
"price":12,
"price_range":"10-50"
}
接下来,搜索请求可以聚合这个新字段,而不是在price字段上运行range聚合。
GET index/type/_search
{
"aggs":{
"price_ranges":{
"terms":{
"field":"price_range"
}
}
}
}
5. 字段映射
有些字段的内容是数字,但并不意味着一定得使用数字类型的字段。一般来说,存储标识符的字段,使用keyword更好。
6. 避免使用脚本
一般来说,应该避免使用脚本。如果一定要使用,则应该优先考虑painless和expressions。
7. 优化日期搜索
在使用日期范围检索时,使用now的查询通常不能缓存,因为匹配到的范围一直在变化。但是,从用户体验的角度来看,切换到一个完整的日期通常是可以接受的,这样可以更好的利用查询缓存。
例如,有下列查询:
PUT index/type/1
{
"createAt":"2020-01-04 15:31:23.369"
}
GET index/type/_search
{
"query":{
"constant_score":{
"filter":{
"range":{
"createAt":{
"gte":"now-1h",
"lte":"now"
}
}
}
}
}
}
可以替换成下面的查询方式:
GET index/type/_search
{
"query":{
"constant_score":{
"filter":{
"range":{
"createAt":{
"gte":"now-1h/m",
"lte":"now/m"
}
}
}
}
}
}
在这个例子中,我们将日期四舍五入到分钟,因此如果当时间是15:32:23
,那么range查询将匹配createAt
字段的值在14:32-15:32
之间的所有内容。如果多个用户同时运行一个包含此查询范围的查询,则查询缓存可以加快查询速度。用于舍入得的时间间隔越长,查询缓存就越有帮助。但是要注意,太高的舍入也可能损害用户体验。
8. 为只读所以执行force-merge
为不再更新的只读索引执行force merge
,将Lucene
索引河北为单个分段,可以提升查询速度。当一个Lucene
索引存在多个分段时,每个分段会单独执行搜索再将结果合并,将只读索引强制合并为一个Lucene
分段不仅可以优化搜索过程,对索引恢复速度也有好处。
基于日期进行轮询的索引的旧数据一般都不会再更新。应该避免持续的写一个固定的索引,然后用别名关联,或者使用索引通配符。这样,可以每天选一个时间点对冷的索引进行force-merge
、Shrink
等操作。
8.1 force-merge操作
$curl -X POST "http://127.0.0.1:9200/index/_forcemerge?max_num_segments=1"
全部merge如下
$curl -X POST "http://127.0.0.1:9200/_forcemerge?max_num_segments=1"
max_num_segments:设置最大segment数量,数量越小,查询速度提高越明显,但merge耗时越长。
8.2 Shrink操作
PUT source_index/_setting
{
"settings":{
"index.routing.allocation.require._name":"node-1",
"index.blocks.write":true
}
}
index.blocks.write
:设置索引为只读。
缩小索引
执行shrink:
POST source_index/_shrink/target_index
{
"settings":{
"index.number_of_replicas":1,
"index.number_of_shards":1,
"index.codec":"best_compression"
},
"aliases":{
"my_search_indices":{}
}
}
9. 预热全局序号(global ordinals)
全局序号是一种数据结构,用于在keyword
字段上运行terms
聚合。它用一个数值来代表字段中的字符串值,然后为每个数值分配一个bucket
,这需要一个对blobal ordinals
和bucket
的构建过程。默认情况下,它们被延迟构建,因为ES不知道哪些字段将用于terms
聚合,哪些字段不会。可以通过配置映射在刷新时告诉ES预先加载全局序数:
PUT index
{
"mappings":{
"type":{
"properties":{
"city":{
"type":"keyword",
"eager_global_ordinals":true
}
}
}
}
}
10. execution hint(执行提示)
terms
聚合有两种不同的机制:
- 通过直接使用字段值来聚合每个桶的数据(map)
- 通过使用字段的全局序号并为每个全局序号分配一个
bucket
ES使用global_ordinals
作为keyword
字段的默认选项,它使用全局序号动态的分配bucket
,因此内存使用与聚合结果中的字段数量是线性关系。在大部分情况下,这种方式的速度很快。当查询只会匹配少量文档时,可以考虑使用map。默认情况下,map只在脚本上运行聚合时使用,因为它们没有序数。
GET index/type/_search
{
"aggs":{
"city":{
"terms":{
"field":"city",
"execution_hint":"map"
}
}
}
}
11. 预热文件系统cache
如果ES主机重启,则文件系统缓存将清空,此时搜索会比较慢,可以使用index.store.preload
设置,通过指定文件扩展名,显示的告诉操作系统应该将哪些文件加载到内存中。
例如,配置到elasticsearch.yml
文件中:
index.store.preload: ["nvd","dvd"]
或者在创建索引的时候设置:
PUT index
{
"settings":{
"index.store.preload":["nvd","dvd"]
}
}
如果文件系统缓存不够大,则无法保存所有数据,那么为太多文件预加载到文件系统中会使搜索速度变慢,应谨慎使用。
12. 使用preference来优化高速缓存利用率
有多个缓存可以帮助提高搜索性能,例如文件系统缓存、请求缓存或查询缓存。
然而,所有这些缓存都维护在节点级别,这意味着如果运行如果运行两次相同的请求,则有一个或多个副本,并使用循环(默认路由算法),那么这2个请求将转发到不同的分片副本,阻止节点级别的缓存帮助。
由于搜索应用程序的用户一个接一个的请求类似的搜索,使相似的搜索请求落到相同的节点,帮助优化高速缓存的使用。
13. 调节搜索请求中的batched_reduce_size
该字段是搜索请求中的一个参数。默认情况下,聚合操作是在协调节点需要等待所有的分片都取回结果后才执行,使用batched_reduce_size
参数可以不等待全部分片返回结果,而是在指定数量的分片返回结果只会就可以先处理一部分(reduce)。这样可以避免协调节点在等待全部结果的过程中占用大量内存,避免极端情况下可能导致的OOM。该字段默认值512,从ES 5.4开始支持。
14. 副本有助于吞吐量
副本除了提高弹性外,也可以帮助提高吞吐量。搜索请求的时候轮询转发到不同的副本。
15. 打开自适应副本选择(ARS)提升ES响应速度
ARS公式为:
每项的含义如下:
-
os(s)
:节点未完成的搜索请求数 -
n
:系统中数据节点的数量 -
R(s)
:响应时间的EWMA,单位为毫秒 -
q(s)
:搜索线程池队列中等待任务数量的EWMA -
μ(s)
:数据节点上的搜索服务时间的EWMA,单位毫秒。
ES通过这些信息大致可以评估出分片副本所在的节点压力和健康度。
ARS从6.1版本开始支持,默认是关闭的,从ES 7.0开始,ARS将默认开启。可以使用下面的命令动态开启:
PUT /_cluster/settings
{
"transient":{
"cluster.routing.use_adaptive_replica_selection":true
}
}
R(s)
:响应时间的EWMA,单位为毫秒
-
q(s)
:搜索线程池队列中等待任务数量的EWMA -
μ(s)
:数据节点上的搜索服务时间的EWMA,单位毫秒。
ES通过这些信息大致可以评估出分片副本所在的节点压力和健康度。
ARS从6.1版本开始支持,默认是关闭的,从ES 7.0开始,ARS将默认开启。可以使用下面的命令动态开启:
PUT /_cluster/settings
{
"transient":{
"cluster.routing.use_adaptive_replica_selection":true
}
}
下一篇: 动态规划-解码方法