搜索性能优化

magic,如果真的要优化搜索性能的话,就是以下几种办法
1/5,配合起来,就是搜索性能优化的杀手锏 3/4,配合起来,解决各种复杂的搜索需求的性能
1、给filesysgtem cache更多的内存
es的搜索引擎严重依赖于底层的filesystem cache,你如果给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,至少半数的情况下,搜索是走内存的,性能一般可以到几秒钟,2秒,3秒,5秒
如果最佳的情况下,我们自己的生产环境实践经验,最好是用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" } } } } } }