1.给文件系统缓存提供更多的内存
es严重依赖文件系统缓存来提高搜索速度。通常,为了使es将搜索保持在物理内存的热区域中,需要确保至少一般的可用内存提供给文件系统缓存
2.使用更快的硬件
如果索引速度受到I/O制约,你需要提供为文件系统缓存提供更多的内存或者升级更快的硬件设备。一般固态比机械硬盘性能更好。始终使用本地文件系统,远程文件系统像NFS以及SMB都应该避免。对例如亚马逊的EBS(elastic block storage)也要留意,es在虚拟存储上表现更好,由于更快以及易于安装使得它变得越来越受欢迎。但是不幸的是相比于本地存储虚拟存储还是有一些差距。如果你要使用EBS请注意要使用提供的IOPS否则会被限流。
如果索引速度受到CPU制约,你需要购买更快的CPU
3.文档建模
文档需要建模这样能够使得索引操作变得更快。
尤其是joins操作应该尽量避免,而嵌套(nested)会降低几倍查询速度,而父子关系(parent-child relations)查询更会使得查询以数百倍的速度下降。所以如果可以使用不规范的文档而不是使用joins也能达到同样的效果,尽量使用这些来提高速度
4.尽量使用更少的字段
query_string和multi_match的字段越多,查询越慢。提高多字段查询速度中普遍使用的一项技术就是将多个字段值拷贝到一个字段,查询的时候只需要查询这个字段即可。这可以使用映射中的copy_to 指令来做到并且该指令不会改变文档源,下面是一个例子
PUT movies
{
"mappings": {
"_doc": {
"properties": {
"name_and_plot": {
"type": "text"
},
"name": {
"type": "text",
"copy_to": "name_and_plot"
},
"plot": {
"type": "text",
"copy_to": "name_and_plot"
}
}
}
}
}
5.预索引数据
你应该使用查询模式优化数据索引方式。例如,如果所有文档都有price字段,并且大多数查询都会使用固定的price 范围查询,那么我们可以事先将该范围映射到索引中来加快聚合此字段,看下面例子:
使用前:
PUT index/_doc/1
{
"designation": "spoon",
"price": 13
}
GET index/_search
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 10 },
{ "from": 10, "to": 100 },
{ "from": 100 }
]
}
}
}
}
使用后:
PUT index
{
"mappings": {
"_doc": {
"properties": {
"price_range": {
"type": "keyword"
}
}
}
}
}
PUT index/_doc/1
{
"designation": "spoon",
"price": 13,
"price_range": "10-100"
}
GET index/_search
{
"aggs": {
"price_ranges": {
"terms": {
"field": "price_range"
}
}
}
}
6.考虑将标识符映射为关键字
事实上一些数值型字段并不一定会被映射为数值型,es在term的数值型范围查询中使用关键字会更好。典型的,字段存储的例如ISBN或者来自另一个数据库的数值型标识符号会很少被用在范围查询或聚合中,这是因为将它们映射为关键字而不是integer或long更好。
7.避免脚本
通常情况下,脚本尽量避免使用,不得已时最好也要使用painless和expressions引擎
8.搜索边界数据
使用now作为日期范围查询一般不会被缓存这是因为now一直在变化。然而就用户体验而言使用边界数据会更好,而且还有利于查询缓存。
使用前:
PUT index/_doc/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"
}
}
}
}
}
}
在这个案例中我们四舍五入到分钟,如果当前时间是16:31:29,这个查询会查询 15:31:00到 16:31:59之间的所有数据。此时如果很多用户进行包含该范围的查询,查询缓存会帮助加快速度。四舍五入的间隔越大,查询缓存越能起到作用。但是太大范围的四舍五入会影响用户体验。
我们也可以使用更大和更小的范围来四舍五入从而提高查询缓存效率
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"should": [
{
"range": {
"my_date": {
"gte": "now-1h",
"lte": "now-1h/m"
}
}
},
{
"range": {
"my_date": {
"gt": "now-1h/m",
"lt": "now/m"
}
}
},
{
"range": {
"my_date": {
"gte": "now/m",
"lte": "now"
}
}
}
]
}
}
}
}
}
但是需要注意的是,有些情况下这种方式会适得其反,因为bool查询(在不同的时间分段中搜索)带来的开销可能会超过查询缓存带来的效率。
9.合并只读索引
将只读索引合并到单个分片中会更好,这种情况在时间索引中尤为明显,因为只有当前时间的索引才会需要写入,过时的索引只需要只读状态。
注意:对于需要写入的索引不要将其合并,另外合并可以作为一个后台进程运行
10.备份全球序数
全球序数是一个作为关键字段被用于terms聚合的数据结构。由于es并不知道哪些字段会被用于terms聚合以及哪些字段不会被用于terms聚合,所以它们一般是被懒加载到内存中。那么你可以使用es在刷新时间通过配置映射来指定某个属性是否被es以全球序数的饥渴状态来加载
PUT index
{
"mappings": {
"_doc": {
"properties": {
"foo": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
}
}
11.备份文件系统缓存
如果机器重启,文件系统缓存会被清空。这会导致操作系统加载索引到热区域之前话费一定的时间。你可以通过index.store.preload配置指定哪些被马上缓存到内存中
注意:如果文件系统缓存不足以装下所有的索引数据,立马加载大量数据到文件系统缓存中会导致查询变慢。
12.使用索引排序加快连接速度
索引排序会牺牲较少的索引速度来加快连接速度
13.使用preference来优化缓存使用率
有很多可以提高查询性能的缓存,例如文件系统缓存,请求缓存或者查询缓存。目前这些缓存都是节点级别的,这意味着连续发送两个相同的请求,在1个或多个副本的情况下,使用round-robin分布式负载均衡策略以及默认的算法,这两个请求会被路由到不同的分片,这会导致节点级别的缓存无法起到作用。
因为对于用户来说一个接一个发送相同请求是很普遍的,所以为了分析索引的较窄子集,使用标识当前用户或会话的偏好值可以帮助优化高速缓存的使用
14.复制可以帮助提高高吞吐但是有条件的
一个节点上分片越少也就意味着为该分片分配的内存越多从而提高吞吐的能力,但是这是以牺牲高可用为前提的,因为缺少了副本分片作为灾备。在这两者之间最好保持平衡,副本分片的最优数量一般是max(max_failures, ceil(num_nodes / num_primaries) - 1) max_failures:每次最多有这么多节点去处理失败。具体见官网
15.打开自适应副本选择
当存在多个副本分片时,es会使用一组称为自适应副本选择的标准,根据包含每个分片副本的节点的响应时间,服务时间和队列大小来选择最佳数据副本。这可以提高查询吞吐量并减少搜索量大的程序的延迟