Elasticsearch 聚合原理

  • 倒排索引(Inverted Index)
    • term dictionary (有序, log(n))
    • term index (FST Trie, O(m))

image.png

  • 列式存储(doc_values)
    • 顺序磁盘读写
    • 序列化压缩
    • analyzed strings 不支持(text)
  • 顺排索引(fielddata)(原理:完整加载这个字段所有 Segment 中的倒排索引到内存中)
    • jvm内存读取,查询性能更高
    • 查询预热
    • indices.fielddata.cache.size =20%(默认没有上限)

配置示例:

  1. {
  2. "properties": {
  3. "tags": {
  4. "type": "keyword",
  5. "doc_values": true // 默认开启
  6. },
  7. "content": {
  8. "type": "text",
  9. "fielddata": true,
  10. "eager_global_ordinals": true // 预加载
  11. },
  12. "extra": {
  13. "keyword",
  14. "index": false
  15. },
  16. "title": {
  17. "type": "text",
  18. "fields": {
  19. "keyword": {
  20. "type": "keyword"
  21. }
  22. }
  23. }
  24. }
  25. }

Aggregations 分类及常用聚合

  • Bucket
    • Terms
  • Composite
  • Multi Terms
  • Histogram
  • Metrics
    1. - Top-hits
  • Stats
  • Cardinality
  • Pipeline aggregations

    • Bucket script

      工单聚合搜索优化过程

      2.1 储存结构优化
  • 关闭动态索引(避免无用字段和全文索引)

  • 分词器调整
  • 通过fields 设置专门的聚合字段

备注:新版本不支持动态查询boost, 可以使用function_score 查询

  1. {
  2. mappings: {
  3. dynamic: false,
  4. properties: {
  5. id: {
  6. type: 'keyword',
  7. boost: 10
  8. },
  9. project: {
  10. type: 'text',
  11. analyzer: 'ik_max_word',
  12. search_analyzer: 'ik_smart',
  13. fields: {
  14. english: {
  15. type: 'text',
  16. analyzer: 'ik_english',
  17. search_analyzer: 'english'
  18. },
  19. pinyin: {
  20. type: 'text',
  21. analyzer: 'ik_pinyin_analyzer',
  22. search_analyzer: 'standard'
  23. }
  24. }
  25. },
  26. approvalId: {
  27. type: 'long'
  28. },
  29. reporter: {
  30. type: 'object',
  31. dynamic: true
  32. },
  33. subject: {
  34. type: 'text',
  35. analyzer: 'ik_max_word',
  36. search_analyzer: 'ik_smart',
  37. boost: 5,
  38. fields: {
  39. english: {
  40. type: 'text',
  41. analyzer: 'ik-english',
  42. search_analyzer: 'english'
  43. },
  44. pinyin: {
  45. type: 'text',
  46. analyzer: 'ik_pinyin_analyzer',
  47. search_analyzer: 'standard'
  48. }
  49. }
  50. },
  51. content: {
  52. type: 'text',
  53. analyzer: 'ik_strip_html',
  54. boost: 1.5,
  55. search_analyzer: 'ik_smart',
  56. fields: {
  57. english: {
  58. type: 'text',
  59. analyzer: 'ik_english',
  60. search_analyzer: 'english'
  61. },
  62. pinyin: {
  63. type: 'text',
  64. analyzer: 'ik_pinyin_analyzer',
  65. search_analyzer: 'standard'
  66. }
  67. }
  68. },
  69. envDescription: {
  70. type: 'flattened'
  71. },
  72. tags: {
  73. type: 'keyword',
  74. fields: {
  75. query: {
  76. type: 'keyword',
  77. normalizer: 'lowercase' // search: case_insensitive: true 7.10
  78. }
  79. }
  80. },
  81. chatGroups: {
  82. type: 'flattened'
  83. },
  84. deletedAt: {
  85. type: 'date'
  86. },
  87. createdAt: {
  88. type: 'date'
  89. },
  90. updatedAt: {
  91. type: 'date'
  92. }
  93. }
  94. },
  95. settings: {
  96. number_of_shards: 5,
  97. number_of_replicas: 2,
  98. analysis: {
  99. analyzer: {
  100. ik_pinyin_analyzer: {
  101. type: 'custom',
  102. tokenizer: 'ik_max_word',
  103. filter: 'pinyin_filter'
  104. },
  105. ik_strip_html: {
  106. type: 'custom',
  107. tokenizer: 'ik_smart',
  108. char_filter: ['html_strip']
  109. },
  110. ik_english: {
  111. type: 'custom',
  112. tokenizer: 'ik_max_word',
  113. filter: ['stemmer'],
  114. stopwords: '_english_'
  115. }
  116. },
  117. filter: {
  118. pinyin_filter: {
  119. type: 'pinyin',
  120. keep_first_letter: false,
  121. keep_separate_first_letter: false,
  122. limit_first_letter_length: 16,
  123. keep_full_pinyin: true,
  124. keep_joined_full_pinyin: true,
  125. keep_none_chinese: false,
  126. keep_none_chinese_together: false,
  127. keep_none_chinese_in_first_letter: false,
  128. keep_none_chinese_in_joined_full_pinyin: false,
  129. none_chinese_pinyin_tokenize: false,
  130. keep_original: false,
  131. lowercase: true,
  132. trim_whitespace: true,
  133. remove_duplicated_term: false
  134. }
  135. }
  136. }
  137. }
  138. }

2.2 搜索查询优化

2.2.1 Tags 聚合优化

  1. {
  2. size: 0,
  3. query: {
  4. bool: {
  5. filter: [
  6. {
  7. prefix: {
  8. 'tags.query': {
  9. value: input
  10. weight:10
  11. }
  12. }
  13. },
  14. {
  15. script: {
  16. script: {
  17. source: `return doc.tags.size() != 0 && doc.deletedAt.size() == 0 && doc.status.value != '${StatusEnum.DRAFT}'`
  18. }
  19. }
  20. }
  21. ]
  22. }
  23. },
  24. aggs: {
  25. genres: {
  26. terms: {
  27. field: 'tags'
  28. }
  29. }
  30. }
  31. }

2.2.2 工单搜索优化
Es评分机制

  • TF:TF(Term Frequency),即词频,表示词条在文本中出现的频率。
  • IDF:IDF(Inverse Document Frequency),即逆文档频率。
  • 字段长度归一值:( norm )是字段中词数平方根的倒数。越短越好
  • 向量空间模型(vector space model): 多词匹配

自定义评分规则: function_score boost_mode

  1. GET /_search
  2. {
  3. "query": {
  4. "dis_max": {
  5. "queries": [
  6. { "term": { "subject": "Quick pets" } },
  7. { "term": { "content": "Quick pets" } }
  8. ],
  9. "tie_breaker": 0.2
  10. }
  11. }
  12. }

2.2.3 常见的查询分析命令

  1. 查看不同分词器分词

    1. curl --location --request POST 'http://elastic:Tiger!23@172.28.49.74:9200/_analyze' \
    2. --data-raw '{
    3. "text": "特朗普",
    4. "analyzer": "ik_max_word"
    5. }'
  2. 查看当前字段分词

    1. curl --location -g --request GET '{{es}}/work-ticket_v7/_doc/2021080600001/_termvectors?fields=content.english'
  3. 查看当前搜索在文档中的评分情况

    1. curl --location -g --request GET '{{es}}/work-ticket_v7/_doc/2021080600001/_termvectors?fields=content.english'
  4. Explain 优化搜索结果相关性

    1. GET /_validate/query?explain {
    2. "query": {
    3. "match" : {
    4. "tweet" : "really powerful"
    5. }
    6. }
    7. }
  5. Profile 定位查询性能问题 ```bash curl XPOST http://localhost:9200/myindex/mytype/_search -d ‘{ “profile”: true, “query”: {

    1. "match": {
    2. "brand": "Cotton Plus"
    3. }

    } }’

```

2.3 关于搜索推荐实现
  • 词条建议器(term suggester):对于给定文本的每个词条,该建议器从索引中抽取要建议的关键词,这对于短字段(如分类标签)很有效。
  • 词组建议器(phrase suggester):我们可以认为它是词条建议器的扩展,为整个文本(而不是单个词条)提供了替代方案,它考虑了各词条彼此临近出现的频率,使得该建议器更适合较长的字段,比如商品的描述。
  • 完成建议器(completion suggester):该建议器根据词条的前缀,提供自动完成的功能(智能提示,有点最左前缀查询的意思),为了实现这种实时的建议功能,它得到了优化,工作在内存中。所以,速度要比之前说的match_phrase_prefix快的多!
  • 上下文建议器(context suggester):它是完成建议器的扩展,允许我们根据词条或分类亦或是地理位置对结果进行过滤。

其它实现方式:

  • match_phrase_prefix
  • ngram 实现搜索推荐

    其它优化调整

  • 项目启动自动更新索引、数据

  • 暴露接口维护旧索引、刷新数据
  • 配置动态词库(本地词库、tags、用户表用户名); (7.12以下版本IK分词注意关闭gzip)

    总结

  • 设置合理的mapping字段类型、分词

  • 使用节点查询缓存(filter)
  • 使用分片请求缓存(size = 0)
  • 通过 msearch 拆解多个聚合为单个子语句
  • 低版本设置 “collect_mode” : “breadth_first”

参考文档:
Terms aggregation | Elasticsearch Guide [7.14]| Elastic
Elastic 系列:Lucene 的索引结构和查询效率 | NingG 个人博客
ElasticSearch Aggregations GroupBy 实现源码分析
通过Function Score Query优化Elasticsearch搜索结果