ES搜索性能优化

 

1、给filesysgtem cache更多的内存

 

es的搜索引擎严重依赖于底层的filesystemcache,你如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。比如说,你,es节点有3台机器,每台机器64G,总内存64 * 3。每台机器给es jvm heap是32G,那么剩下来留给filesystem cache的就是每台机器才32g,总共集群里给filesystem cache的就是32 * 3 = 96gb内存。如果此时你整个磁盘上索引数据文件,在3台机器上一共占用了1T的磁盘容量,es数据量是1t。filesystem cache的内存才100g,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。

归根结底,你要让es性能要好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半

比如说,你一共要在es中存储1T的数据,那么你的多台机器留个filesystem cache的内存加起来综合,至少要到512G,至少半数的情况下,搜索是走内存的,性能一般可以到几秒钟。

如果最佳的情况下,我们自己的生产环境实践经验,最好是用es就存少量的数据,就是你要用来搜索的那些索引,内存留给filesystem cache的,就100G,那么你就控制在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内

所以尽量在es里,就存储必须用来搜索的数据,比如说你现在有一份数据,有100个字段,其实用来搜索的只有10个字段,建议是将10个字段的数据,存入es,剩下90个字段的数据,可以放mysql,hadoop hbase,都可以。

这样的话,es数据量很少,10个字段的数据,都可以放内存,就用来搜索,搜索出来一些id,通过id去mysql,hbase里面去查询明细的数据

 

2、用更快的硬件资源

(1)给filesystem cache更多的内存资源

(2)用SSD固态硬盘

(3)使用本地存储系统,不要用NFS等网络存储系统

(4)给更多的CPU资源

 

3、document模型设计

document模型设计是非常重要的,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es能支持的操作就是那么多,不要考虑用es做一些它不好操作的事情。如果真的有那种操作,尽量在document模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。

 

在搜索/查询的时候,要执行一些业务强相关的特别复杂的操作:

1)在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入加的字段里面

2)自己用java程序封装,es能做的,用es来做,搜索出来的数据,在java程序里面去做,比如说我们,基于es,用java封装一些特别复杂的操作

 

4、预先index data

为了性能,提前优化data index时的数据模型,比如说document有一个price field,然后大多数查询都对一个固定的范围,对这个field使用range范围查询,那么可以提前将这个price的范围处理出来,写入一个字段中。比如下面这样:

 

PUT index/type/1

{

 "designation": "spoon",

 "price": 13

}

 

GET index/_search

{

 "aggs": {

   "price_ranges": {

     "range": {

       "field": "price",

       "ranges": [

         { "to": 10 },

         { "from": 10, "to": 100 },

         { "from": 100 }

       ]

     }

    }

  }

}

 

我们完全可以增加一个price_range字段:

 

PUT index

{

 "mappings": {

   "type": {

     "properties": {

       "price_range": {

         "type": "keyword"

       }

     }

    }

  }

}

 

然后写入的时候,直接计算出来这个range:

 

PUT index/type/1

{

 "designation": "spoon",

 "price": 13,

 "price_range": "10-100"

}

 

然后搜索的时候,就可以直接用term查询了,性能非常高:

 

GET index/_search

{

 "aggs": {

   "price_ranges": {

     "terms": {

       "field": "price_range"

     }

    }

  }

}

 

5、预热filesystem cache

如果我们重启了es,那么filesystem cache是空壳的,就需要不断的查询才能重新让filesystem cache热起来,我们可以先说动对一些数据进行查询。比如说,你本来一个查询,要用户点击以后才执行,才能从磁盘加载到filesystem cache里,第一次执行要10s,以后每次就几百毫秒。你完全可以,自己早上的时候,就程序执行那个查询,预热,数据就加载到filesystem cahce,程序执行的时候是10s,以后用户真的来看的时候就才几百毫秒。

 

6、避免使用script脚本

说实话,一般是避免使用es script的,实际生产中更是少用,性能不高,尽量不要使用

 

7、使用固定范围的日期查询

尽量不要使用now这种内置函数来执行日期查询,因为默认now是到毫秒级的,是无法缓存结果,尽量使用一个阶段范围,比如now/m,就是到分钟级

 

那么如果一分钟内,都执行这个查询,是可以取用查询缓存的

 

PUT index/type/1

{

 "my_date": "2016-05-11T16:30:55.328Z"

}

 

GET index/_search

{

 "query": {

   "constant_score": {

     "filter": {

       "range": {

         "my_date": {

           "gte": "now-1h",

           "lte": "now"

         }

       }

     }

    }

  }

}

 

完全可以替换为:

 

GET index/_search

{

 "query": {

   "constant_score": {

     "filter": {

       "range": {

         "my_date": {

           "gte": "now-1h/m",

           "lte": "now/m"

         }

       }

     }

    }

  }

}

 

 

 

磁盘读写性能优化

优化磁盘空间的占用,减少磁盘空间的占用,更多的数据可以进入filesystem cache。比如说你原来,磁盘空间占用一共是1T,内存只有512G,现在优化了磁盘空间占用之后,减少了数据量,可能数据量就只有512G了,那么就可以全部进入内存

 

1、禁用不需要的功能

 

聚合,搜索,评分,近似匹配

 

聚合:doc values

搜索:倒排索引,index

评分:norms

近似匹配:index_options(freqs)

 

任何一个功能不需要,就把对应的存储的数据给干掉,这样可以节约磁盘空间的占用,也可以优化磁盘的读写性能

 

默认情况下,es在写入document到索引的时候,都会给大多数的field增加一份doc values,就是正排索引,用来进行聚合或者排序的。比如说,如果我们有一个叫做foo的数字类型field,我们要对这个字段运行histograms aggr聚合操作,但是可能我们并不需要对这个字段进行搜索,那么就可以禁止为这个字段生成倒排索引,只需要doc value正排索引即可。

 

禁用倒排索引:

 

PUT index

{

  "mappings": {

    "type": {

      "properties": {

        "foo": {

          "type":"integer",

          "index": false

        }

      }

    }

  }

}

 

 

text类型的field会存储norm值,用来计算doc的相关度分数,如果我们需要对一个text field进行搜索,但是不关心这个field的分数,那么可以禁用norm

 

PUT index

{

  "mappings": {

    "type": {

      "properties": {

        "foo": {

          "type": "text",

          "norms": false

        }

      }

    }

  }

}

 

 

text field还会存储出现频率以及位置,出现频率也是用来计算相关度分数的,位置是用来进行phrase query这种近似匹配操作的,如果我们不需要执行phrase query近似匹配,那么可以禁用位置这个属性:

 

PUT index

{

  "mappings": {

    "type": {

      "properties": {

        "foo": {

          "type": "text",

          "index_options":"freqs"

        }

      }

    }

  }

}

 

 

 

此外,如果我们不关心相关度频分,我们可以配置es仅仅为每个term索引对应的document,我们可以对这个field进行搜索,但是phrasequery这种近似匹配会报错,而且相关度评分会不准确:

 

PUT index

{

  "mappings": {

    "type": {

      "properties": {

        "foo": {

          "type": "text",

          "norms": false,

          "index_options":"freqs"

        }

      }

    }

  }

}

 

2、不要用默认的动态string类型映射

 

默认的动态string类型映射会将string类型的field同时映射为text类型以及keyword类型,这会浪费磁盘空间,因为我们不一定两种都需要。通常来说,id field这种字段可能只需要keyword映射,而body field可能只需要text field。

 

映射一个content,content: text,content.内置字段: keyword

 

可以通过手动设置mappings映射来避免字符串类型的field被自动映射为text和keyword:

 

PUT index

{

 "mappings": {

   "type": {

     "dynamic_templates": [

       {

         "strings": {

           "match_mapping_type": "string",

           "mapping": {

              "type":"keyword"

           }

         }

       }

     ]

    }

  }

}

 

3、禁止_all field

 

_all field会将document中所有field的值都合并在一起进行索引,很耗费空空间,如果不需要一次性对所有的field都进行搜索,那么最好禁用_all field。

 

4、使用best_compression

 

_source field和其他field都很耗费磁盘空间,最好是对其使用best_compression进行压缩。用elasticsearch.yml中的index.codec来设置,将其设置为best_compression即可。

 

5、用最小的最合适的数字类型

 

es支持4种数字类型,byte,short,integer,long。如果最小的类型就合适,那么就用最小的类型。

你可能感兴趣的:(ES搜索性能优化)