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里面去查询明细的数据
(1)给filesystem cache更多的内存资源
(2)用SSD固态硬盘
(3)使用本地存储系统,不要用NFS等网络存储系统
(4)给更多的CPU资源
document模型设计是非常重要的,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es能支持的操作就是那么多,不要考虑用es做一些它不好操作的事情。如果真的有那种操作,尽量在document模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。
在搜索/查询的时候,要执行一些业务强相关的特别复杂的操作:
(1)在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入加的字段里面
(2)自己用java程序封装,es能做的,用es来做,搜索出来的数据,在java程序里面去做,比如说我们,基于es,用java封装一些特别复杂的操作
为了性能,提前优化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"
}
}
}
}
如果我们重启了es,那么filesystem cache是空壳的,就需要不断的查询才能重新让filesystem cache热起来,我们可以先说动对一些数据进行查询。比如说,你本来一个查询,要用户点击以后才执行,才能从磁盘加载到filesystem cache里,第一次执行要10s,以后每次就几百毫秒。你完全可以,自己早上的时候,就程序执行那个查询,预热,数据就加载到filesystem cahce,程序执行的时候是10s,以后用户真的来看的时候就才几百毫秒。
说实话,一般是避免使用es script的,实际生产中更是少用,性能不高,尽量不要使用
尽量不要使用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了,那么就可以全部进入内存
聚合,搜索,评分,近似匹配
聚合: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"
}
}
}
}
}
默认的动态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"
}
}
}
]
}
}
}
_all field会将document中所有field的值都合并在一起进行索引,很耗费空空间,如果不需要一次性对所有的field都进行搜索,那么最好禁用_all field。
_source field和其他field都很耗费磁盘空间,最好是对其使用best_compression进行压缩。用elasticsearch.yml中的index.codec来设置,将其设置为best_compression即可。
es支持4种数字类型,byte,short,integer,long。如果最小的类型就合适,那么就用最小的类型。