【Elasticsearch系列】5分钟掌握查询原理-lucece查询逻辑介绍

这篇具有很好参考价值的文章主要介绍了【Elasticsearch系列】5分钟掌握查询原理-lucece查询逻辑介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🎬作者简介:大家好,我是蓝胖子🥇

☁️博客首页:CSDN主页蓝胖子的编程梦

🌄每日一句:既然选择了追求,就不要哭泣。坚持一下,扛过今天,幸福就更近一步

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

大家好,我是蓝胖子,最近在做一些elasticsearch 慢查询优化的事情,通常用分析elasticsearch 慢查询的时候可以通过profile api 去分析,分析结果显示的底层lucene在搜索过程中使用到的函数调用。所以要想彻底弄懂elasticsearch慢查询的原因,还必须将lucene的查询原理搞懂,今天我们就先来介绍下lucene的查询逻辑的各个阶段。

lucene 查询过程分析

先放上一张查询过程的流程图,下面的分析其实都是对这张图的更详细的介绍。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

lucene的查询可以大致分为4个阶段,重写查询,创建查询weight对象,创建scorer对象准备计分,进行统计计分。

简单解释下这4个阶段;

1, 重写查询语句( rewrite query )

lucene提供了比较丰富的外部查询类型,像wildcardQuery,MatchQuery等等,但它们最后都会替换为比较底层的查询类型,例如wildcardQuery会被重写为MultiTermsQuery。

2, 创建查询weight对象( createWeight )

Query对象创建的权重对象, lucece的每个查询都会计算一个该查询占用的权重值,如果是不需要计分的,则权重值是一个固定常量,得到的文档结果是根据多个查询的权重值计算其得分的。下面是Weight 对象涉及的方法,

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

其中,scorer(LeafReaderContext context) 方法是个抽象方法,需要子类去实现的。

public abstract Scorer scorer(LeafReaderContext context) throws IOException;

方法返回的scorer对象拥有遍历倒排列表和统计文档得分的功能,下面会讲到实际上weight对象是创建BulkScore进行计分的,但BulkScore内部还是通过score对象进行计分。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

再详细解释下Scorer对象中比较重要的方法;

  • iterator() 方法返回的DocIdSetIterator 对象提供了遍历倒排列表的能力。如下是DocIdSetIterator 涉及的方法,其中docID()是为了返回当前遍历到的倒排列表的文档id,nextDoc()则是将遍历指针移动到下一个文档,并且返回文档id,advance 用于移动遍历指针。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

  • twoPhaseIterator 方法提供对文档二次精准匹配的能力,比如在matchPhrase查询中,不但要查出某个词,还要求查出的词之间相对顺序不变,那么这个相对顺序则是通过twoPhaseIterator的matches方法去进行判断。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

3, 创建bulkScorer对象( weight.bulkScore)

weight 对象会调用BulkScore方法创建BulkScorer对象,bulkScorer 内部首先调用的是scorer抽象方法(需要由weight子类去实现的方法),得到的scorer对象再拿去构建DefaultBulkScorer 对象,所以说,实际上最后计分的还是通过scorer对象进行计分的。

public BulkScorer bulkScorer(LeafReaderContext context) throws IOException {  
  
  Scorer scorer = scorer(context);  
  if (scorer == null) {  
    // No docs match  
    return null;  
  }  
  
  // This impl always scores docs in order, so we can  
  // ignore scoreDocsInOrder:  
  return new DefaultBulkScorer(scorer);  
}

bulkScorer类有如下方法,一个是提供对段所有文档进行计分,一个是可以在段的某个文档id范围内进行计分。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

4, 进行统计计分

最后则是通过collector对象进行统计,这里提到了collecor对象,它其实是作为了上述bulkScorer的score方法参数传入的,在bulkScore.score方法内部,遍历文档时,对筛选出的文档会通过调用collector.collect(doc)方法进行收集,在collect方法内部,则是调用scorer对象对文档进行打分。

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece
完整的搜索流程如下

public <C extends Collector, T> T search(Query query, CollectorManager<C, T> collectorManager)  
    throws IOException {  
  final C firstCollector = collectorManager.newCollector();  
  // 重写查询对象
  query = rewrite(query, firstCollector.scoreMode().needsScores());  
  // 调用indexSearch的createWeight方法,本质上还是调用的Query的createWeight方法
  final Weight weight = createWeight(query, firstCollector.scoreMode(), 1);  
  return search(weight, collectorManager, firstCollector);  
}

// 简化了代码,保留了主流程,调用scorer.score 进行计分。
protected void search(List<LeafReaderContext> leaves, Weight weight, Collector collector){
	// 得到每个segment段的收集器,源代码是可以在线程池中同时对几个segment进行搜索的,这里省略了。
	leafCollector = collector.getLeafCollector(ctx);
	BulkScorer scorer = weight.bulkScorer(ctx);
	// 将收集器作为buklScore.score参数传入,对文档进行计分。
	scorer.score(leafCollector, ctx.reader().getLiveDocs());
	leafCollector.finish();
}

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

profile api 返回结果分析

理清楚了lucene的搜索逻辑,我们再来看看通过profile api返回的各个阶段耗时是统计的哪段逻辑。

在使用elasticsearch 的profile api 时,会返回如下的统计阶段

lucene 引擎如何执行查询,Elasticsearch系列,elasticsearch,lucene,搜索引擎,大数据,mysql,全文检索,lucece

如果不了解源码可能会对这些统计指标比较疑惑,结合刚才对lucece 源码的了解来看下几个比较常见的统计指标。

next_doc 是取倒排链表中当前遍历到的文档id,并且把遍历的指针移动到下一个文档id消耗的时长。

score 是weight.scorer方法创建的score对象,进行文档计分的操作时消耗的时长。

match 是 twoPhaseIterator进行二次匹配判断时消耗的时长。

advance 是直接将遍历的指针移动到特定文档id处消耗的时长。

build_score 是weight对象在通过weight.scorer方法创建score对象时所耗费的时长。

create_weight 是query对象在调用其自身createWeight方法创建weight对象时耗费的时长。

set_min_competitive_score,compute_max_score,shallow_advance 我也还没彻底弄懂它们用到的所有场景,这里暂不做分析。

这里还要注意的一点是,像布尔查询是结合了多个子查询的结果,它内部会构造特别的scorer对象,比如ConjunctionScorer 交集scorer,它的next_doc 方法则是需要对其子查询的倒排链表求交集,所以你在用profile api 分析时,可能会看到布尔查询的next_doc 耗时较长,而其子查询耗时较长的逻辑则是advance,因为倒排列表合并逻辑会有比较多的advance移动指针的动作。

profile api 的实现原理

最后,我再来谈谈elasticsearch 是如何实现profile 的,lucene的搜索都是通过IndexSearcher对象来执行的,IndexSearcher在调用query对象自身的rewrite 方法重写query后,会调用IndexSearcher 的createWeight 方法来创建weight对象(本质上底层还是使用的query的createWeight方法)。

elasticsearch 继承了IndexSearcher ,重写了createWeight,在原本weight对象的基础上,封装了一个profileWeight对象。以下是关键代码。

public Weight createWeight(Query query, ScoreMode scoreMode, float boost) throws IOException {  
    if (profiler != null) {  
        // createWeight() is called for each query in the tree, so we tell the queryProfiler  
        // each invocation so that it can build an internal representation of the query        // tree        QueryProfileBreakdown profile = profiler.getQueryBreakdown(query);  
        Timer timer = profile.getNewTimer(QueryTimingType.CREATE_WEIGHT);  
        timer.start();  
        final Weight weight;  
        try {  
            weight = query.createWeight(this, scoreMode, boost);  
        } finally {  
            timer.stop();  
            profiler.pollLastElement();  
        }  
        return new ProfileWeight(query, weight, profile);  
    } else {  
        return super.createWeight(query, scoreMode, boost);  
    }  
}

基于文章开头的lucene查询逻辑分析,可以知道,scorer对象最后也是通过weight对象的scorer方法得到的,所以创建出来的profileWeight的scorer方法通用也对返回的scorer对象封装了一层,返回的是profileScorer对象。

public Scorer scorer(LeafReaderContext context) throws IOException {  
    ScorerSupplier supplier = scorerSupplier(context);  
    if (supplier == null) {  
        return null;  
    }  
    return supplier.get(Long.MAX_VALUE);  
}  
  
@Override  
public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException {  
    final Timer timer = profile.getNewTimer(QueryTimingType.BUILD_SCORER);  
    timer.start();  
    final ScorerSupplier subQueryScorerSupplier;  
    try {  
        subQueryScorerSupplier = subQueryWeight.scorerSupplier(context);  
    } finally {  
        timer.stop();  
    }  
    if (subQueryScorerSupplier == null) {  
        return null;  
    }  
  
    final ProfileWeight weight = this;  
    return new ScorerSupplier() {  
  
        @Override  
        public Scorer get(long loadCost) throws IOException {  
            timer.start();  
            try {  
                return new ProfileScorer(weight, subQueryScorerSupplier.get(loadCost), profile);  
            } finally {  
                timer.stop();  
            }  
        }  
  
        @Override  
        public long cost() {  
            timer.start();  
            try {  
                return subQueryScorerSupplier.cost();  
            } finally {  
                timer.stop();  
            }  
        }  
    };  
}

剩下的就好办了,在profileScore对象里对scorer对象的原生方法前后加上时间统计即可对特定方法进行计时了。比如下面代码中profileScore的advanceShallow方法。

public int advanceShallow(int target) throws IOException {  
    shallowAdvanceTimer.start();  
    try {  
        return scorer.advanceShallow(target);  
    } finally {  
        shallowAdvanceTimer.stop();  
    }  
}

总结

通过本篇文章,应该可以对lucene的查询过程有了大概的了解,但其实对于elasticsearch的慢查询分析还远远不够,因为像布尔查询,wilcard之类的比较复杂的查询,我们还得弄懂,它们底层是究竟如何把一个大查询分解成小查询的。才能更好的弄懂查询耗时的原因,所以在下一节,我会讲解这些比较常见的查询类型的内部重写和查询逻辑。文章来源地址https://www.toymoban.com/news/detail-835424.html

到了这里,关于【Elasticsearch系列】5分钟掌握查询原理-lucece查询逻辑介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一起学Elasticsearch系列-聚合查询

    本文已收录至Github,推荐阅读 👉 Java随想录 微信公众号:Java随想录 聚合查询是 Elasticsearch 中一种强大的数据分析工具,用于从索引中提取和计算有关数据的统计信息。聚合查询可以执行各种聚合操作,如计数、求和、平均值、最小值、最大值、分组等,以便进行数据汇总和

    2024年01月22日
    浏览(39)
  • 【ElasticSearch系列-03】ElasticSearch的高级句法查询Query DSL

    ElasticSearch系列整体栏目 内容 链接地址 【一】ElasticSearch下载和安装 https://zhenghuisheng.blog.csdn.net/article/details/129260827 【二】ElasticSearch概念和基本操作 https://blog.csdn.net/zhenghuishengq/article/details/134121631 【二】ElasticSearch的高级查询Query DSL https://blog.csdn.net/zhenghuishengq/article/details/1

    2024年02月06日
    浏览(48)
  • ElasticSearch系列 - SpringBoot整合ES:组合多个查询条件 bool 查询

    01. ElasticSearch 布尔查询是什么? 在实际应用中,我们很有可能会查询多个值或字段。 一个 bool 查询由三部分组成: must:所有的语句都必须(must) 匹配,与 AND 等价。 must_not:所有的语句都不能(must not)匹配,与 NOT 等价。 should:至少有一个语句要匹配,与 OR 等价。 02.

    2023年04月08日
    浏览(57)
  • Elasticsearch(十二)搜索---搜索匹配功能③--布尔查询及filter查询原理

    本节主要学习ES匹配查询中的布尔查询以及布尔查询中比较特殊的filter查询及其原理。 复合搜索,顾名思义是一种在一个搜索语句中包含一种或多种搜索子句的搜索。 布尔查询是常用的复合查询,它把多个子查询组合成一个布尔表达式,这些子查询之间的逻辑关系是\\\"与\\\",即

    2024年02月04日
    浏览(38)
  • 五、浅析[ElasticSearch]底层原理与分组聚合查询

    集群节点介绍 es配置文件夹中 客户端节点 当主节点和数据节点配置都设置为false的时候,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。独立的客户端节点在一个比较大的集群中是非常有用的,他协调主节点和数据节

    2024年02月16日
    浏览(39)
  • 架构师系列-搜索引擎ElasticSearch(四)- 高级查询

    ES查询 该方式可以通过kabana、curl、elasticsearch-head(纯前端)去操作 term查询和字段类型有关系,首先回顾一下ElasticSearch两个数据类型 ElasticSearch两个数据类型 1、text:会分词,不支持聚合 2、keyword:不会分词,将全部内容作为一个词条,支持聚合 term查询:不会对查询条件进

    2024年04月15日
    浏览(55)
  • Elasticsearch 系列(四)- DSL实现自动补全查询

    本章将和大家分享如何通过 Elasticsearch 实现自动补全查询功能。 1、自动补全需求说明 当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项,如图: 2、使用拼音分词 要实现根据字母做补全,就必须对文档按照拼音分词。在 GitHub 上恰好有 Elasticsearch 的 拼音分

    2024年03月17日
    浏览(33)
  • ElasticSearch系列 - SpringBoot整合ES:多个精确值查询 terms

    ElasticSearch - SpringBoot整合ES:多个精确值查询 terms 01. ElasticSearch terms 查询支持的数据类型 在Elasticsearch中,terms查询支持多种数据类型,包括: 字符串类型:可以将多个字符串值作为数组传递给terms查询,以匹配包含任何一个指定字符串值的文档。 数值类型:可以将多个数值作

    2024年02月16日
    浏览(59)
  • SpringBoot ElasticSearch 实现订单的分页查询 【SpringBoot系列17】

    SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。 程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海

    2024年02月09日
    浏览(35)
  • ElasticSearch系列 - SpringBoot整合ES之全文搜索匹配查询 match

    官方文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/index.html 权威指南:https://www.elastic.co/guide/cn/elasticsearch/guide/current/structured-search.html 1. 数据准备 官方测试数据下载地址:https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip ,数据量很大,我们自己构造数据吧。 2. m

    2023年04月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包