elasticsearch 官方优化建议

打印 上一主题 下一主题

主题 1527|帖子 1527|积分 4581

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1.一般建议

  a.不要返回过大的结果集。这个建议对一般数据库都是适用的,如果要获取大量结果,可以使用search_after api,或者scroll (新版本中已经不推荐)。
  b.避免大的文档。
2. 如何提高索引速度

  a.使用批量请求。为了达到最好的效果,可以进行测试,递增地提高bulk的数量,比如从100,到200,再到400,达到一个吞吐量和响应时间的平衡。
  b.使用多线程发送数据。
  c.关闭或者减小refresh_interval。从内存缓存写入磁盘缓存(memorybuffer -> filesystem cache),这个过程叫做refresh。在这个过程之前内存缓存里面的文档是不可被搜索的,这也是为什么es被称为近实时索引的原因。
    在索引初始化(大量导入文档)的时候,可以关闭refresh_interval。当产品允许较大的不可搜索时间,可以将index.refresh_interval设置为30s,提高索引速度。
  d.初始化时关闭复制分片。索引时设置index.number_of_replicas为0,避免主分片复制数据,索引完毕后再调整到正常的复制分片数。
  e.关闭swapping。swap会极大地降低es的索引速度。
  1. Swap分区(即交换区)在系统的物理内存不够用的时候,把硬盘空间中的一部分空间释放出来,以供当前运行的程序使用。
  2. 那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。
复制代码
  f.给文件系统缓存分配足够多的内存。文件系统换行用来处理io操作,至少要将物理机一半的内存分配给文件系统缓存。比如物理机内存64g,那么至少分配32g给文件系统缓存,剩下的内存才考虑分配给es。
  g.使用自动生成的id。如果使用指定的id,es会检查这个id是否已经存在,而且随着文档数越多,这个判重操作越耗时。索引的时候,如果没有指定id,es会自动生成id。
  1. {
  2.     "_index": "sales",
  3.     "_type": "_doc",
  4.     "_id": "xb7IY4cB6Rdc8HbDycuE", // auto-generated id
  5.     "_version": 1,
  6.     "result": "created",
  7.     "_shards": {
  8.         "total": 2,
  9.         "successful": 1,
  10.         "failed": 0
  11.     },
  12.     "_seq_no": 10,
  13.     "_primary_term": 1
  14. }
复制代码
  h.使用更好的硬件。比如SSD,或者Amazon的Elastic Block Storage。
  i.调整索引缓存大小。确保每个索引分片能获得512M的缓存,即 indices.memory.index_buffer_size = 512M,大于512M没有更多提升效果。
  j.使用cross-cluster replication 来实现读写分离,这样让索引集群压力更小。这和mysql中的读写分离很类似。
3.如何提到搜索速度

  a.给文件系统缓存分配足够多的内存。
  b.在linux环境中设置合适的readahead。但是es中的查询更多的是随机io,过大的readahead反而使文件系统的页缓存严重抖动,从而使查询性能下降。
  1. Linux的文件预读readahead,指Linux系统内核将指定文件的某区域预读进页缓存起来,便于接下来对该区域进行读取时,不会因缺页(page fault)而阻塞。因为从内存读取比从磁盘读取要快很多。
  2. 预读可以有效的减少磁盘的寻道次数和应用程序的I/O等待时间,是改进磁盘读I/O性能的重要优化手段之一。使用命令lsblk查看readahead值。
复制代码
  c.使用更好的硬件。
  d.好的文档模型。酌情使用nested query, parent query, 避免使用join query。
文档模型对比普通查询
nested query慢几倍
parent query慢几百倍
join query应当避免
  e.尽可能少的查询字段。在越多的字段上匹配,查询速度就越慢。在索引的时候可以将需要查询的多个字段聚合到一个字段中。使用copy_to 可以自动实现这一功能,以下示例将name和plot字段聚合到name_and_plot字段中。
  1. PUT movies
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "name_and_plot": {
  6.         "type": "text"
  7.       },
  8.       "name": {
  9.         "type": "text",
  10.         "copy_to": "name_and_plot"
  11.       },
  12.       "plot": {
  13.         "type": "text",
  14.         "copy_to": "name_and_plot"
  15.       }
  16.     }
  17.   }
  18. }
复制代码
  f.预先索引数据。比如如果想对price字段做range聚合,那么预先计算出单个文档的price范围,那么就能将range聚合转化成terms聚合。这样确实能提高效率,但是不太灵活。
插入文档:
  1. PUT index/_doc/1
  2. {
  3.   "designation": "spoon",
  4.   "price": 13
  5. }
复制代码
range聚合查询:
  1. GET index/_search
  2. {
  3.   "aggs": {
  4.     "price_ranges": {
  5.       "range": {
  6.         "field": "price",
  7.         "ranges": [
  8.           { "to": 10 },
  9.           { "from": 10, "to": 100 },
  10.           { "from": 100 }
  11.         ]
  12.       }
  13.     }
  14.   }
  15. }
复制代码
另一种做法,预先计算price_range:
  1. PUT index
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "price_range": {
  6.         "type": "keyword"
  7.       }
  8.     }
  9.   }
  10. }
  11. PUT index/_doc/1
  12. {
  13.   "designation": "spoon",
  14.   "price": 13,
  15.   "price_range": "10-100"
  16. }
复制代码
使用terms聚合:
  1. GET index/_search
  2. {
  3.   "aggs": {
  4.     "price_ranges": {
  5.       "terms": {
  6.         "field": "price_range"
  7.       }
  8.     }
  9.   }
  10. }
复制代码
  g.尽可能将字段自定义为keyword。对于数字类型的字段,es对其range查询做了优化。在term层级的查询下,keyword字段比数字类型要好。
    在以下两种情况下可以考虑将数字类型定义为keyword:
      1.不需要对这些数据进行range查询
      2.有很高的查询速度要求。
    如果实在不清楚哪个好,可以用 multi-field为数字类型的字段同时定义数字类型和keyword类型。
  h.避免使用脚本。如果可能,避免使用脚本排序,使用脚本聚合,以及script_scorequery。
  i.使用四舍五入的日期。这样有助于es进行缓存,精确到秒级别的查询有时候并无必要。
实时查询(秒级):
  1. PUT index/_doc/1
  2. {
  3.   "my_date": "2016-05-11T16:30:55.328Z"
  4. }
  5. GET index/_search
  6. {
  7.   "query": {
  8.     "constant_score": {
  9.       "filter": {
  10.         "range": {
  11.           "my_date": {
  12.             "gte": "now-1h",
  13.             "lte": "now"
  14.           }
  15.         }
  16.       }
  17.     }
  18.   }
  19. }
复制代码
分钟级查询:
  1. GET index/_search
  2. {
  3.   "query": {
  4.     "constant_score": {
  5.       "filter": {
  6.         "range": {
  7.           "my_date": {
  8.             "gte": "now-1h/m",
  9.             "lte": "now/m"
  10.           }
  11.         }
  12.       }
  13.     }
  14.   }
  15. }
复制代码
  j.对只读索引进行force-merge。在时序索引中,过期的索引都是只读的,将其合并成一个段能加快查询速度。
  k.预热global ordinals。ordinals 是doc values的具体存储形式。一般情况下一个字段的global ordinals是懒加载的。如果某个字段在聚合上用到很多,我们可以先将其预热(加载到heap),当做field data cache.的一部分。
  1. PUT index
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "foo": {
  6.         "type": "keyword",
  7.         "eager_global_ordinals": true
  8.       }
  9.     }
  10.   }
  11. }
复制代码
  l.预热文件系统缓存。设置index.store.preload参数即可。注意,必须确保文件系统缓存足够大,否则会让查询变得更慢。
  m.使用索引排序来加速连接查询。比如我们要进行过滤 a AND b AND …​,然后a是low-cardinality(低区分度)。那么我们可以先对a进行排序,那么一旦a的某个值不匹配这个表达式,那么有相同的值的文档都可以跳过。
  n.使用preference进行缓存使用优化。es中有非常多的缓存,比如文件系统缓存(最重要),请求缓存,查询缓存,但是这些缓存都是在节点层面。默认情况下es会使用round-robin算法分配查询到不同的分片上去,这样缓存就失效了。
    如果可以,使用preference参数将用户的请求和对应的分片或者节点绑定起来,这样缓存就不会失效。例如:
  1. GET /_search?preference=_shards:2,3
  2. {
  3.     "query": {
  4.         "match": {
  5.             "title": "elasticsearch"
  6.         }
  7.     }
  8. }
复制代码
  o.更多的复制分片会提升吞吐量(但并不一定)。在系统资源充足的情况下,复制分片越多吞吐量会越高。但是过多的分片会让故障恢复变得更慢。
  p.使用profile api优化查询语句。和mysql中的explain类似,例如:
  1. GET /my-index-000001/_search
  2. {
  3.   "profile": true,
  4.   "query" : {
  5.     "match" : { "message" : "GET /search" }
  6.   }
  7. }
  8. {
  9.   "took": 25,
  10.   "timed_out": false,
  11.   "_shards": {
  12.     "total": 1,
  13.     "successful": 1,
  14.     "skipped": 0,
  15.     "failed": 0
  16.   },
  17.   "hits": {
  18.     "total": {
  19.       "value": 5,
  20.       "relation": "eq"
  21.     },
  22.     "max_score": 0.17402273,
  23.     "hits": [...]
  24.   },
  25.   "profile": {
  26.     "shards": [
  27.       {
  28.         "id": "[2aE02wS1R8q_QFnYu6vDVQ][my-index-000001][0]",
  29.         "searches": [
  30.           {
  31.             "query": [
  32.               {
  33.                 "type": "BooleanQuery",
  34.                 "description": "message:get message:search",
  35.                 "time_in_nanos" : 11972972,
  36.                 "breakdown" : {
  37.                   "set_min_competitive_score_count": 0,
  38.                   "match_count": 5,
  39.                   "shallow_advance_count": 0,
  40.                   "set_min_competitive_score": 0,
  41.                   "next_doc": 39022,
  42.                   "match": 4456,
  43.                   "next_doc_count": 5,
  44.                   "score_count": 5,
  45.                   "compute_max_score_count": 0,
  46.                   "compute_max_score": 0,
  47.                   "advance": 84525,
  48.                   "advance_count": 1,
  49.                   "score": 37779,
  50.                   "build_scorer_count": 2,
  51.                   "create_weight": 4694895,
  52.                   "shallow_advance": 0,
  53.                   "create_weight_count": 1,
  54.                   "build_scorer": 7112295
  55.                 },...
复制代码
  q.使用 index_phrases 加速phrase query。index_phrases,会将两个单词的组合单独索引,这样可以加速phrase query。
  r.使用 index_phrases 加速prefix query。同上。
  s.使用constant_keyword加速过滤。如果某个字段的大多数情况下的值是个常量,但是我们又经常要对其进行过滤,我们可以将其拆分成两个索引,一个使用constant_keyword,一个不使用。
mapping如下:
  1. UT bicycles
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "cycle_type": {
  6.         "type": "constant_keyword",
  7.         "value": "bicycle"
  8.       },
  9.       "name": {
  10.         "type": "text"
  11.       }
  12.     }
  13.   }
  14. }
  15. PUT other_cycles
  16. {
  17.   "mappings": {
  18.     "properties": {
  19.       "cycle_type": {
  20.         "type": "keyword"
  21.       },
  22.       "name": {
  23.         "type": "text"
  24.       }
  25.     }
  26.   }
  27. }
复制代码
查询语句:
  1. GET bicycles,other_cycles/_search
  2. {
  3.   "query": {
  4.     "bool": {
  5.       "must": {
  6.         "match": {
  7.           "description": "dutch"
  8.         }
  9.       },
  10.       "filter": {
  11.         "term": {
  12.           "cycle_type": "bicycle"
  13.         }
  14.       }
  15.     }
  16.   }
  17. }
复制代码
在查询bicycles索引时,es会将查询语句自动转换为:
  1. GET bicycles,other_cycles/_search
  2. {
  3.   "query": {
  4.     "match": {
  5.       "description": "dutch"
  6.     }
  7.   }
  8. }
复制代码
4.磁盘优化

  a.禁用不需要的特性。
    比如数字类型的字段如果不需要进行过滤,可以不对其进行索引。
  1. PUT index
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "foo": {
  6.         "type": "integer",
  7.         "index": false
  8.       }
  9.     }
  10.   }
  11. }
复制代码
    es会对text类型的字段存储一些打分信息,如果不需要对这些字段进行打分,可以将其设置为match_only_text类型
  b.不要使用默认动态字符串映射。默认动态字符串映射会将字符串类型映射为text和keyword类型,这样很浪费空间。可以预先配置所有字符串映射类型为keyword。
  1. PUT index
  2. {
  3.   "mappings": {
  4.     "dynamic_templates": [
  5.       {
  6.         "strings": {
  7.           "match_mapping_type": "string",
  8.           "mapping": {
  9.             "type": "keyword"
  10.           }
  11.         }
  12.       }
  13.     ]
  14.   }
  15. }
复制代码
  c.监控分片大小。越大的分片能更有效地存储数据。但是分片越大,故障恢复也会越慢。
  d.禁用_source字段。_source会存储原始的json数据,如果不需要,就将其禁用。
  e.使用best_compression进行压缩。es默认使用 LZ4 进行压缩,使用best_compression可以提升压缩比率,但是会影响数据存取性能。
  f.force-merge.强制合并段能提升存储效率。注意,force-merge应当在没有文件写入后进行,  比如在过期的时序索引节点上。
  g.shrink 索引。即收缩索引,将当前索引重新索引成分片数更少的索引。分片越大,存储效率越高。
    shrink索引有如下条件。
    1.索引必须只读。
    2.节点必须包含索引的所有分片(主分片,或者复制分片都可以)
    3.索引状态必须是健康的。
  h.使用能满足需求的最小的数字类型。比如能用byte, 不用short。这个在其他db比如mysql中也适用。
  i.使用索引排序来提升文档的压缩性能。排序后相似的文档会放在一起,es能根据他们的特性有效地进行压缩。
    设定索引排序:
  1. PUT my-index-000001
  2. {
  3.   "settings": {
  4.     "index": {
  5.       "sort.field": "date",
  6.       "sort.order": "desc"  
  7.     }
  8.   },
  9.   "mappings": {
  10.     "properties": {
  11.       "date": {
  12.         "type": "date"
  13.       }
  14.     }
  15.   }
  16. }
复制代码
  j.索引文档时保证json字段顺序一致。es在存储的时候将多个文档压缩成一成block,如果json文档顺序一致,es能更好的对更长的相同的字符串进行压缩。
  k.roll-up历史数据。使用roll up api来归档历史数据,他们依然可以访问,但是有着更高的存储效率。
5.分片大小

  1.将索引分片大小保持在10G~50G之间
  2.平均下来每G堆内存下不要超过20个分片。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

干翻全岛蛙蛙

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表