ElasticSearch系列:如何优化因为高亮造成的大文本(大字段)检索缓慢问题

一、前言

正在学习性能优化,发现一篇好文章,转载记录下:
如何优化因为高亮造成的大文本(大字段)检索缓慢问题

ElasticSearch版本:8.11.1

二、正文

首先还是说一下背景,工作中用到了 elasticsearch 的检索以及高亮展示,但是索引中的content字段是读取的大文本内容,所以后果就是索引的单个字段很大,造成单独检索请求的时候速度还可以,但是加入高亮之后检索请求的耗时就非常的慢了。所以本文从更换高亮器类型的角度来解决因为高亮造成的检索请求缓慢的问题。

1、ES的抵消策略

在文章开始前先简单介绍一个elasticsearch的策略,为了在检索的字段中创建出一个有意义的高亮片段,高亮器会使用原始文本的开始和结束字符串的偏移量,偏移量的获取可以从一下方式获得

  1. postings list:如果在mappingindex_options设置为offsetsunified高亮器使用此信息高亮显示文档而不用再次分析文本。
  2. term vectors:如果我们在mapping中设置term_vectorwith_positions_offsets,则unified高亮器会自动使用term_vector来高亮显示,对于大于1M的大字段,使用term_vector速度会很快,fvh高亮器就是使用的term_vector
  3. plain highlighting:当unified没有其他的选择的时候会使用plain模式,它会创建了一个微小的内存索引,并通过Lucene的查询执行计划器重新运行原始查询条件。plain高亮器默认使用的就是此模式

大文本的纯高亮展示可能需要大量的时间和内存,为了防止这种情况,es默认将大文本的字符数量限制为1000000,可以使用index.highlight.max_analyzed_offset修改此默认设置

2、FVH高亮器简介

FVH(Fast Vector Highlighter)是Elasticsearch高亮器中的一种算法,使用的是Lucene Fast Vector highlighter,它能够快速而准确地在文本中找到匹配的关键词,并将其标记为高亮。相比于其他高亮器算法,FVH在性能上有着显著的优势,特别适用于大规模数据集和高并发的场景。

3、FVH高亮器的使用方法

3.1、安装

首先,确保已经正确安装了 Elasticsearch

version: '3.8'
services:
  cerebro:
    image: lmenezes/cerebro:0.8.3
    container_name: cerebro
    ports:
     - "9000:9000"
    command:
     - -Dhosts.0.host=http://eshot:9200
    networks:
     - elastic
  kibana:
    image: docker.elastic.co/kibana/kibana:8.1.3
    container_name: kibana
    environment:
      - I18N_LOCALE=zh-CN
      - XPACK_GRAPH_ENABLED=true
      - TIMELION_ENABLED=true
      - XPACK_MONITORING_COLLECTION_ENABLED="true"
      - ELASTICSEARCH_HOSTS=http://eshot:9200
      - server.publicBaseUrl=http://192.168.160.234:5601
    ports:
      - "5601:5601"
    networks:
      - elastic
  eshot:
    image: elasticsearch:8.1.3
    container_name: eshot
    environment:
      - node.name=eshot
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=eshot,eswarm,escold
      - cluster.initial_master_nodes=eshot,eswarm,escold
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
      - node.attr.node_type=hot
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - D:\zuiyuftp\docker\es8.1\eshot\data:/usr/share/elasticsearch/data
      - D:\zuiyuftp\docker\es8.1\eshot\logs:/usr/share/elasticsearch/logs
      - D:\zuiyuftp\docker\es8.1\eshot\plugins:/usr/share/elasticsearch/plugins
    ports:
      - 9200:9200
    networks:
      - elastic
  eswarm:
    image: elasticsearch:8.1.3
    container_name: eswarm
    environment:
      - node.name=eswarm
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=eshot,eswarm,escold
      - cluster.initial_master_nodes=eshot,eswarm,escold
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
      - node.attr.node_type=warm
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - D:\zuiyuftp\docker\es8.1\eswarm\data:/usr/share/elasticsearch/data
      - D:\zuiyuftp\docker\es8.1\eswarm\logs:/usr/share/elasticsearch/logs
      - D:\zuiyuftp\docker\es8.1\eshot\plugins:/usr/share/elasticsearch/plugins
    networks:
      - elastic
  escold:
    image: elasticsearch:8.1.3
    container_name: escold
    environment:
      - node.name=escold
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=eshot,eswarm,escold
      - cluster.initial_master_nodes=eshot,eswarm,escold
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
      - node.attr.node_type=cold
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - D:\zuiyuftp\docker\es8.1\escold\data:/usr/share/elasticsearch/data
      - D:\zuiyuftp\docker\es8.1\escold\logs:/usr/share/elasticsearch/logs
      - D:\zuiyuftp\docker\es8.1\eshot\plugins:/usr/share/elasticsearch/plugins
    networks:
      - elastic

# volumes:
#   eshotdata:
#     driver: local
#   eswarmdata:
#     driver: local
#   escolddata:
#     driver: local

networks:
  elastic:
    driver: bridge

3.2、创建索引

在使用FVH高亮器之前,需要先创建一个索引,并将需要高亮的字段进行映射。例如,我们要在content字段中进行高亮,可以使用以下代码:

PUT /example_target
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "ik_max_word",
        "term_vector": "with_positions_offsets"
      },
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",
        "term_vector": "with_positions_offsets"
      }
    }
  }
}

3.3、添加测试数据

POST example_target/_doc
{
  "content":"中华人民共和国是否考虑是否就爱上速度加快分解ask计算机卡死撒中华上的飞机拉丝机是的地方记录 卡就是开发建设看积分卡说了句 ask就疯狂萨拉丁就发士大 sdf 看得见啊李开复 圣诞节卡了 夫哈数据库中华啊,中华,人民爱上中华",
  "title":"中华人名共和国"
}

3.4、查询并高亮

使用FVH高亮器进行查询和高亮的过程如下所示:

GET example_target/_search
{
  "query": {
    "match": {
      "content": "中华 爱上"
    }
  },
  "highlight": {
    "pre_tags": "<em>",
    "post_tags": "</em>", 
    "require_field_match": "false", 
    "fields": {
      "content": {
         "type": "fvh",
        "fragment_size": 18,
        "number_of_fragments": 3
      }
    }
  }
}

以上代码中,我们通过match查询找到了包含关键词的文档,然后在highlight内容中指定了需要高亮的字段,这里是content。执行述查询后,Elasticsearch将返回匹配的结果,并在content字段中添加了高亮标记。

数据量少的时候对比不是特别明显,所以在测试时,可以在索引中添加大量的测试数据进行测试,本人在测试过程中es的索引大小在500M左右,单个字段纯文本大小也有1-2M。此时这种数据规模下使用普通的高亮器在检索请求时就已经非常缓慢了,根据返回的数据量多少来决定,在取10条数据时已经能达到6秒了,但是在使用fvh高亮器之后时间已经进入毫秒级

4、FVH高亮器的参数配置

先看一下返回的数据结果在对照下面参数学习

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.41193593,
    "hits" : [
      {
        "_index" : "example_target",
        "_id" : "f1rkC4oBCDmhQc2yo6PQ",
        "_score" : 0.41193593,
        "_source" : {
          "content" : "中华人民共和国是否考虑是否就爱上速度加快分解ask计算机卡死撒中华上的飞机拉丝机是的地方记录 卡就是开发建设看积分卡说了句 ask就疯狂萨拉丁就发士大 sdf 看得见啊李开复 圣诞节卡了 夫哈数据库中华啊,中华,人民爱上中华"
        },
        "highlight" : {
          "content" : [
            "<em>中华</em>人民共和国是否考虑是否就<em>爱上</em>速度",
            "sk计算机卡死撒<em>中华</em>上的飞机拉丝机是的地方记录",
            "夫哈数据库<em>中华</em>啊,<em>中华</em>,人民<em>爱上</em>中华"
          ]
        }
      },
      {
        "_index" : "example_target",
        "_id" : "G3Fi44kB4IVEhjafHXOf",
        "_score" : 0.33311102,
        "_source" : {
          "content" : "中华人民共和国是否考虑是否就爱上速度加快分解ask计算机卡死撒中华上的飞机拉丝机是的地方记录卡就是开发建设看积分卡说了句ask就疯狂萨拉丁就发士大夫哈数据库"
        },
        "highlight" : {
          "content" : [
            "<em>中华</em>人民共和国是否考虑是否就<em>爱上</em>速度",
            "sk计算机卡死撒<em>中华</em>上的飞机拉丝机是"
          ]
        }
      },
      {
        "_index" : "example_target",
        "_id" : "HHFt44kB4IVEhjafE3Ov",
        "_score" : 0.31932122,
        "_source" : {
          "content" : "中华人民共和国是否考虑是否就爱上速度加快分解ask计算机卡死撒中华上的飞机拉丝机是的地方记录 卡就是开发建设看积分卡说了句 ask就疯狂萨拉丁就发士大 sdf 看得见啊李开复 圣诞节卡了 夫哈数据库"
        },
        "highlight" : {
          "content" : [
            "<em>中华</em>人民共和国是否考虑是否就<em>爱上</em>速度",
            "sk计算机卡死撒<em>中华</em>上的飞机拉丝机是的地方记录"
          ]
        }
      }
    ]
  }
}

通过上面的查询请求中高亮参数的指定可以发现,高亮器还是支持其他的参数的,那么我们下面将对几个常用的参数进行说明:

  • fragment_size:指定每个高亮片段的长度,默认为100个字符。

  • number_of_fragments:指定返回的高亮片段数量,默认为5个。

  • pre_tagspost_tags:分别指定高亮标记的前缀和后缀,默认为<em></em>

  • require_field_match:指定是否要求所有字段都匹配关键词才进行高亮,默认为true。可以开启关闭此参数对上面的title字段进行校验

  • type:指定fvh高亮器,除了fvh之外还有unifiedplain

    • unified 是默认的高亮器,可以将文本分解为句子,并使用BM25算法对单个句子进行评分,还支持精确的短语高亮显示,支持(fuzzy,prefix,regex)高亮。
    • plain 普通的高亮器,适用与简单的查询或者单个字段的匹配。为了准确的反应查询逻辑,它会在内存中创建一个很小的索引,来对原始的查询语句进行执行,来访问当前更低级别的匹配信息。

在使用FVH高亮器时,根据实际需求,可以灵活地调整这些参数,以获得最佳的高亮效果。

5、总结

通过本文的介绍,我们了解了Elasticsearch高亮器中的FVH算法,并学会了如何使用它为搜索结果增添亮点。FVH高亮器在性能和功能上都有着明显的优势,对于大规模数据集和高并发的场景尤为适用。希望读者通过本文的指引,能够更好地利用FVH高亮器来提升搜索结果的可读性和用户体验。

6、参考链接

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/highlighting.html

三、结束语

nice!

<think>好的,用户让我详细介绍一下Elasticsearch中的Highlighter,也就是高亮显示功能。首先,我需要回顾一下之前用户的问题历史,之前他们询问过ES的数据静态加密,现在转向了搜索相关的高亮功能,说明用户可能在使用ES进行搜索业务,并且对搜索结果的处理有需求。 用户可能是开发人员或者运维人员,负责实现搜索功能,需要了解如何高亮显示匹配的关键词,提升用户体验。他们可能已经配置了基本的搜索,现在想要优化结果展示,让用户更容易看到匹配的部分。 接下来,我需要确定Highlighter的基本概念、支持的类型、使用方法和配置参数。要确保覆盖不同高亮器的区别,比如默认的unified,以及plain、fvh,还有它们各自的适用场景和限制。同时,用户可能需要知道如何通过REST API或者Kibana来使用高亮,以及如何处理一些常见问题,比如性能影响或HTML转义。 要注意用户可能关心的是如何快速实现高亮,是否需要额外的配置,以及不同高亮器的性能差异。比如,fvh需要存储term_vector,可能会增加索引小,但适合复杂查询。而unified是ES 7.0之后的默认选项,更高效,可能更适合部分场景。 另外,用户可能会遇到高亮不准确的问题,比如分词器不匹配导致的高亮错误,这时候需要解释如何确保查询和高亮使用相同的分析器。或者,他们可能需要自定义高亮标签,比如用<em>代替默认的<em>,这需要调整pre_tags和post_tags。 还要考虑是否需要提到快照高亮、边界控制(比如最分析偏移量),以及如何在高亮中处理多字段的情况。例如,如果字段有多个子字段,不同的分析器可能会影响高亮结果,这时候需要指导用户如何正确指定字段。 最后,需要总结最佳实践,帮助用户根据具体需求选择合适的高亮器,并注意性能优化。可能还需要提醒用户测试不同的配置,特别是在数据量下的表现,以及如何处理高亮截断和片段合并的情况。 确保内容结构清晰,分步骤说明,符合用户之前得到的数据静态加密详解的结构,比如分点列出核心概念、类型、配置参数、示例、注意事项等。同时,使用中文,避免复杂术语,保持易懂,并附上示例代码,方便用户直接参考使用。</think>以下是关于 **Elasticsearch Highlighter(高亮显示)** 的详细解析,分步骤说明其核心概念、使用方法和实践技巧: --- ### 1. **高亮显示的作用** - **核心功能**:在搜索结果中标记匹配的关键词,帮助用户快速定位匹配内容。 - **适用场景**: - 全文检索(如日志分析、文档搜索)。 - 关键词突出显示(如电商平台的商品搜索)。 --- ### 2. **ES 高亮器类型** | 类型 | 描述 | 适用场景 | |--------------------|----------------------------------------------------------------------|------------------------------| | **Unified** | 默认高亮器,基于 Lucene UnifiedHighlighter,支持多字段和复杂查询 | 通用场景,推荐优先使用 | | **Plain** | 基于 Lucene SimpleHighlighter,简单但功能较少 | 基础查询(如 `match` 查询) | | **Fast Vector (FVH)** | 利用词向量(Term Vectors)加速,支持复杂片段组合和边界控制 | 需要高亮字段或复杂匹配逻辑 | --- ### 3. **高亮配置参数** #### (1) **基础参数** ```json GET /your_index/_search { "query": { "match": { "content": "elasticsearch" } }, "highlight": { "fields": { "content": { "type": "unified", // 指定高亮器类型(默认 unified) "pre_tags": [""], // <em>高亮起始标签 "post_tags": ["</em>"], // 高亮结束标签 "fragment_size": 150, // 返回片段长度(字符数) "number_of_fragments": 3 // 返回片段数量 } } } } ``` #### (2) **高级参数** | 参数 | 说明 | |------------------------|----------------------------------------------------------------------| | `boundary_scanner` | 控制片段边界(如 `chars`、`sentence`、`word`) | | `boundary_max_scan` | 扫描边界的最字符数(默认 20) | | `no_match_size` | 当无匹配时返回的字段前缀长度(默认 0) | | `require_field_match` | 是否仅高亮查询匹配的字段(默认 `true`,设为 `false` 可跨字段高亮) | --- ### 4. **高亮器对比与选择** | **特性** | Unified | Plain | FVH | |-----------------------|-----------------------|----------------------|----------------------| | **性能** | 高 | 中 | 低(需存储词向量) | | **支持复杂查询** | 是(如布尔、短语查询) | 否 | 是 | | **词向量依赖** | 否 | 否 | 是 | | **多字段优化** | 是 | 否 | 否 | --- ### 5. **示例:不同高亮器使用** #### (1) **Unified 高亮器** ```json GET /logs/_search { "query": { "match_phrase": { "message": "error connecting" } }, "highlight": { "fields": { "message": { "type": "unified", "fragment_size": 200, "number_of_fragments": 2 } } } } ``` #### (2) **FVH 高亮器(需启用词向量)** ```json PUT /docs { "mappings": { "properties": { "text": { "type": "text", "term_vector": "with_positions_offsets" // 必须启用词向量 } } } } // 查询时指定 FVH "highlight": { "fields": { "text": { "type": "fvh" } } } ``` --- ### 6. **常见问题优化** #### (1) **高亮不准确** - **原因**:查询与字段分析器不一致(如查询使用 `standard`,字段使用 `ik_smart`)。 - **解决**:指定高亮时强制使用分析器: ```json "highlight": { "fields": { "content": { "type": "unified", "analyzer": "ik_smart" // 显式指定分析器 } } } ``` #### (2) **性能瓶颈** - **优化方向**: - 避免高亮文本字段(如超过 1MB 的文本)。 - 减少 `fragment_size` 和 `number_of_fragments`。 - 对高频查询字段使用 `store: true` 提升读取速度。 #### (3) **HTML 转义** - **问题**:若字段包含 HTML 标签,高亮可能破坏结构。 - **解决**:设置 `encoder` 参数: ```json "highlight": { "encoder": "html", // 自动转义 HTML 字符 "fields": { ... } } ``` --- ### 7. **最佳实践** 1. **优先使用 Unified 高亮器**:功能全面且性能较好。 2. **控制高亮范围**:合理设置 `fragment_size` 和 `number_of_fragments`。 3. **词向量谨慎启用**:仅在需要 FVH 时配置 `term_vector`,避免存储开销。 4. **测试分析器一致性**:确保查询和高亮阶段使用相同的分词规则。 5. **监控性能**:关注高亮操作对查询延迟的影响(可通过 `Profile API` 分析)。 --- ### 8. **总结** Elasticsearch高亮功能通过灵活的参数配置支持多种场景,核心在于根据数据特性(如字段小、查询复杂度)选择合适的高亮器类型。对于多数场景,默认的 Unified 高亮器已足够高效,但在处理字段或复杂逻辑时,可结合 FVH 和词向量优化效果。实践中需平衡性能与功能需求,并注意分析器一致性等细节问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值