ES - Search的运行机制
Search执行的时候实际分两个步骤运作的
Query阶段
- node3在接收到用户的search请求后,会先进行Query阶段(此时是CoordinatingNode角色)
- node3在6个主副分片中随机选择3个分片,发送search request
- 被选中的3个分片会分别执行查询并排序,返回from+size个文档Id和排序值
- node3 整合3个分片返回的from+size个文档id,根据排序值排序后选取from到from+size的文档id
Fetch 阶段
- node3根据Query阶段获取的文档Id列表去对应的shard 上获取文档详情数据
- node3向相关的分片发送 multi_get请求
- 3个分片返回文档详细数据
- node3拼接返回的结果并返回给客户
相关性算分问题
- 相关性算分在 shard 与 shard 间是相互独立的,也就意味着同一个 Term 的 IDF 等值在不同 shard 上是不同的。文档的相关性算分和它所处的 shard 相关
- 在文档数量不多时,会导致相关性算分严重不准的情况发生(在不同的shard上面)
- 解决思路有两个:
- 设置分片数为1个,从根本上排除问题,在文档数量不多的时候可以考虑该方案,比如百万到千万级别的文档数量
- 使用DFS Query-then-Fetch查询方式
- DFSQuery-then-Fetch 是在拿到所有文档后再重新完整的计算一次相关性算分,耗费更多的 cpu 和内存,执行性能也比较低下,一般不建议使用。使用方式如下
排序
es默认会采用相关性算分排序,用户可以通过设定sorting参数来自行设定排序规则
按照字符串排序比较特殊,因为es有 text和 keyword 两种类型,针对 text 类型排序,如下所示:
排序的过程实质是对字段原始内容排序的过程,这个过程中倒排索引无法发挥作用,需要用到正排索引,也就是通过文档id 和字段可以快速得到字段原始内容
- es对此提供了2种实现方式
- fielddata 默认禁用
- doc values 默认启用,除了 text 类型
对比 | FieldData | DocValues |
---|---|---|
创建时机 | 搜索时即时创建 | 索引时创建,与倒排索引创建时机一致 |
创建位置 | JVM Heap | 磁盘 |
优点 | 不会占用额外的磁盘资源 | 不会占用 Heap 内存 |
缺点 | 文档过多时,即时创建会花过多时间,占用过多Heap内存 | 减慢索引的速度,占用额外的磁盘资源 |
Fielddata默认是关闭的,可以通过如下api开启︰
- 此时字符串是按照分词后的term排序,往往结果很难符合预期·一般是在对分词做聚合分析的时候开启
Doc Values 默认是启用的,可以在创建索引的时候关闭如果后面要再开启 doc values,需要做 reindex 操作作
可以通过 docvalues_fields 获取fielddata 或者doc values中存储的内容
分页与遍历
es提供了3种方式来解决分页与遍历的问题
- from/size
- scroll
- search_after
最常用的分页方案
- from指明开始位置
- size指明获取总数
深度分页
- 深度分页是一个经典的问题:在数据分片存储的情况下如何获取前1000个文档?
- 获取从990~1000的文档时,会在每个分片上都先获取1000个文档,然后再由 Coordinating Node 聚合所有分片的结果后再排序选取前1000个文档
- 页数越深,处理文档越多,占用内存越多,耗时越长。尽量避免深度分页,es 通过
index.max_result_window
限定最多到 10000条数据
Scroll
遍历文档集的 api,以快照的方式来避免深度分页的问题
- 不能用来做实时搜索,因为数据不是实时的
- 尽量不要使用复杂的 sort 条件,使用_doc 最高效
- 使用稍嫌复杂
第一步需要发起1个 scroll search,如下所示
- es 在收到该请求后会根据查询条件创建文档 Id 合集的快照
- 第二步调用 scroll search 的 api,获取文档集合,如下所示不断迭代调用直到返回 hits.hits 数组为空时停止
- 过多的 scroll 调用会占用大量内存,可以通过 clear api 删除过多的 scroll 快照
Search After
避免深度分页的性能问题,提供实时的下一页文档获取功能
缺点是不能使用from参数,即不能指定页数
只能下一页,不能上一页
使用简单
第一步为正常的搜索,但要指定 sort 值,并保证值唯一
第二步为使用上一步最后一个文档的 sort 值进行查询
如何避免深度分页问题 ?
- 通过唯一排序值定位将每次要处理的文档数都控制在 size 内
类型 | 场景 |
---|---|
From/Size | 需要实时获取顶部的部分文档,且需要自由翻页 |
Scroll | 需要全部文档,如导出所有数据的功能 |
Search_After | 需要全部文档,不需要自由翻页 |