CodeAshen's blog CodeAshen's blog
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)

CodeAshen

后端界的小学生
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)
  • MySQL8.0详解与实战

  • MySQL面试指南

  • Redis从入门到高可用

  • Elastic-Stack

    • 第01章-Elasticsearch入门
    • 第02章-倒排索引与分词
    • 第03章-Mapping设置
    • 第04章-Search API
    • 第05章-分布式特性介绍
    • 第06章-Search的运行机制
      • 3.1 ES 排序操作
      • 3.2 FieldData 和 DocValues
        • 3.2.1 Fielddata
        • 3.2.2 Doc Values
        • 3.2.3 docvalue_fields 获取存储内容
      • 4.1 from/size
      • 4.2 scroll
      • 4.3 search_after
    • 第07章-聚合分析
    • 第08章-数据建模
    • 第09章-集群调优建议
  • 数据库
  • Elastic-Stack
CodeAshen
2023-02-10
目录

第06章-Search的运行机制

# 一、Search 两个阶段

Search 执行的时候实际分两个步骤运作的,即 Query-Then-Fetch

  • Query 阶段:查询出文档id
  • Fetch 阶段:拉取文档数据

Query 阶段过程:

image-20201227032936521

注意:上述步骤中选取的 3 个分片,必须包含索引的完整数据,即 0, 1, 2 分片必须都有,但是可以是主分片也可以是副本分片

Fetch 阶段过程:

image-20201227033057665

# 二、相关性算分问题

相关性算分在 shard 与 shard 间是相互独立的,也就意味着同一个 Term 的 IDF 等值在不同 shard 上是不同的。文档的相关性算分和它所处的 shard 相关。

在文档数量不多时,会导致相关性算分严重不准的情况发生。

解决思路有两个:

  1. 设置分片数为 1 个,从根本上排除问题,在文档数量不多的时候可以考虑该方案,比如百万到干万级别的文档数量
  2. 使用 DFS Query-then-Fetch 查询方式

DFS Query-then-Fetch 是在拿到所有文档后再重新完整的计算一次相关性算分,耗费更多的 cpu 和内存,执行性能也比较低下,一般不建议使用。使用方式如下:

image-20201227033703844

# 三、排序

# 3.1 ES 排序操作

es 默认会采用相关性算分排序,用户可以通过设定 sorting 参数来自行设定排序规则

image-20201227033849683

image-20201227033954663

按照字符串排序比较特殊,因为 es 有 text 和 keyword 两种类型。如果针对 text 类型排序,如下所示:

image-20201227034328731

针对 keyword 类型排序,可以返回预期结果:

image-20201227034408381

# 3.2 FieldData 和 DocValues

排序的过程实质是对字段原始内容排序的过程,这个过程中 倒排索引无法发挥作用,需要用到 正排索引,也就是通过文档 id 和字段可以快速得到字段原始内容,然后按照原始内容进行排序。

es 对此提供了 2 种实现方式:

  • fielddata,默认禁用
  • doc values,默认启用,除了text类型

image-20201227034644517

# 3.2.1 Fielddata

Fielddata 默认是关闭的,可以通过如下 api 开启:

  • 此时字符串是按照分词后的 term 排序,往往结果很难符合预期
  • 一般是在对分词做聚合分析的时候开启

image-20201227034851533

# 3.2.2 Doc Values

Doc Values 默认是启用的,可以在创建索引的时候关闭,如果后面要再开启 doc values,需要做 reindex 操作。如果明确知道不会按照某个字段进行排序,可以考虑关闭 doc values,最大限度的加快索引速度,减少磁盘占用。

image-20201227035133503

# 3.2.3 docvalue_fields 获取存储内容

可以通过该字段获取 fielddata 或者 doc values 中存储的内容

image-20201227035344484

# 四、分页与遍历

es 提供了 3 种方式来解决分页与遍历的问题:

  • from/size
  • scroll
  • search_after
类型 场景
From/Size 需要实时获取顶部的部分文档,且需要自由翻页
Scroll 需要全部文档,如导出所有数据的功能
Search_After 需要全部文档,不需要自由翻页

# 4.1 from/size

最常用的分页方案,from 指明开始位置,size 指明获取总数

image-20201227040011543

深度分页问题

深度分页是一个经典的问题:在数据分片存储的情况下如何获取前 1000 个文档?

  • 获取从 990~1000 的文档时,会在每个分片上都先获取 1000 个文档,然后再由 Coordinating Node 聚合所有分片的结果后再排序选取前 1000 个文档
  • 页数越深,处理文档越多,占用内存越多,耗时越长。尽量避免深度分页,es 通过 index.max result window 限定最多到 10000 条数据

image-20201227040232980

# 4.2 scroll

遍历文档集的 api,以快照的方式来避免深度分页的问题。

  • 不能用来做实时搜索,因为数据不是实时的
  • 尽量不要使用复杂的 sort 条件,使用 _doc 最高效
  • 使用稍显复杂

使用步骤:

  1. 第一步需要发起一个 scroll search,es 在收到该请求后会根据查询条件创建文档 id 合集的快照,并返回一个 scroll id

    image-20201227040800272

  2. 第二步使用 scroll id 进行 scorll 操作,获取数据集合和下一批次的 scroll id,不断迭代调用直到返回 hits.hits 数组为空时停止。

    这里每次调用都可以在此指定快照时间,用于刷新快照有效时间,防止失效。

    image-20201227040918387

过多的 scroll 调用会占用大量内存,可以通过 clear api 删除过多的 scroll 快照:

image-20201227041039308

# 4.3 search_after

search_after 也避免深度分页的性能问题,并提供实时的下一页文档获取功能。使用此功能搜索,必须指定排序值,并且排序值必须唯一。

  • 缺点是不能使用from参数,即不能指定页数
  • 只能下一页,不能上一页
  • 使用简单

使用步骤如下:

  1. 为正常的搜索,但要指定 sort 值,并保证值唯一
  2. 为使用上一步最后一个文档的 sort 值进行查询

image-20201227041244726

search_after 是如何避免深度分页问题的?

通过唯一排序值定位将每次要处理的文档数都控制在 size 内。即指定了排序值之后,每个分片只需要返回排序值之后的 size 条数据即可,不需要返回前 n 条完整数据。

image-20201227041458696

编辑 (opens new window)
上次更新: 2023/06/04, 12:34:19
第05章-分布式特性介绍
第07章-聚合分析

← 第05章-分布式特性介绍 第07章-聚合分析→

最近更新
01
第01章-RabbitMQ导学
02-10
02
第02章-入门RabbitMQ核心概念
02-10
03
第03章-RabbitMQ高级特性
02-10
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CodeAshen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式