当前位置: 首页 > news >正文

lucene 8.7.0 版本中的倒排索引、数字、DocValues三种类型的查询性能对比

我们来详细对比一下 Lucene 8.7.0 中倒排索引、数字(Points)和 DocValues 这三种核心数据结构在查询性能上的差异。

首先,一个关键的理念是:它们被设计用来解决不同的问题,因此性能对比必须基于具体的应用场景。 将它们放在不适合的场景下对比,性能差异会非常悬殊。

下面我们逐一分析,然后进行总结和场景化对比。


1. 倒排索引 (Inverted Index)

倒排索引是 Lucene 的基石,主要为文本搜索而生。

  • 核心数据结构:

    • Term Dictionary (词典):一个包含了所有文档中经过分词、处理后的所有 Term (词项) 的有序列表。在 Lucene 8.x 中,通常使用 FST (Finite State Transducer) 结构,它极大地压缩了词典的存储空间,并能快速定位 Term。
    • Postings List (倒排列表):对于词典中的每一个 Term,都有一个列表记录了包含该 Term 的所有文档 ID。这个列表还可能包含 Term 在文档中出现的频率 (freq)、位置 (position)、偏移量 (offset) 等信息。
  • 主要用途:

    • 全文检索:快速找到包含特定词语的文档。例如:"search engine"
    • 关键词精确匹配:对于 keyword 类型的字段(不分词),可以快速找到字段值完全匹配的文档。例如:status: "published"
  • 查询性能特点:

    • 极快:对于查找包含某个 Term 的文档,其性能非常高。查询过程是:
      1. 在 FST 词典中快速定位 Term (类似 O(logN) 或更快)。
      2. 获取指向 Postings List 的指针。
      3. 遍历 Postings List 得到所有匹配的文档 ID。
    • 性能与 Term 的稀有度相关
      • 稀有 Term (Low-frequency Term):查询极快,因为 Postings List 很短。
      • 常见 Term (High-frequency Term):查询相对较慢,因为 Postings List 很长,需要处理和合并更多的文档 ID。例如,搜索 “的” 会比搜索 “Lucene” 慢得多。
    • 不适合范围查询:对于文本,没有“范围”的概念。对于数字或日期,如果用倒排索引存储(老版本 Lucene 的做法),范围查询会变成一个巨大的布尔查询(OR 连接范围内所有的 Term),性能非常低下。
  • 典型查询: TermQuery, BooleanQuery, PhraseQuery, MatchQuery (in Elasticsearch)


2. 数字类型 (Points)

从 Lucene 6.x 开始,引入了 Points 类型来专门处理数值、日期、地理坐标等多维数据。它彻底改变了 Lucene 处理数值范围查询的方式。

  • 核心数据结构: BKD 树 (Block K-D Tree)。这是一种为多维空间数据检索优化的平衡树结构。

    • 对于数字,是一维 BKD 树。
    • 对于地理坐标,是二维 BKD 树。
  • 主要用途:

    • 数值、日期的范围过滤:例如,查找价格在 [100, 500] 之间的商品,或者2023年的订单。
    • 地理空间位置过滤:例如,查找某个点周围5公里内的所有店铺。
  • 查询性能特点:

    • 范围查询极快:BKD 树的结构使得范围查询的复杂度大致为 O(logN),其中 N 是文档总数。它能够非常高效地剪掉不符合范围条件的文档块,无需逐一检查。
    • 性能与范围大小无关:查询一个很小的范围(如 [100, 101])和一个很大的范围(如 [0, 10000])的性能差异不大。这与倒排索引形成鲜明对比。
    • 精确值查询也很快:相当于一个范围为 [N, N] 的查询。
    • 不用于全文检索:它不存储原始文本,也不进行分词。
  • 典型查询: PointRangeQuery (in Elasticsearch)


3. DocValues

DocValues 是一个“正向”的索引结构,也被称为“列式存储”。它将字段值按文档 ID 进行了组织。

  • 核心数据结构: 列式存储 (Columnar Storage)。对于一个字段,它存储的是一个从 文档ID -> 字段值 的映射。

    • 例如,一个 price 字段的 DocValues 可能看起来像:
      • Doc 0 -> 10.0
      • Doc 1 -> 25.5
      • Doc 2 -> 10.0
    • 为了优化存储和访问,它会根据数据类型(数字、关键词)进行压缩和编码。
  • 主要用途:

    • 排序 (Sorting):当需要按某个字段排序时,可以直接通过 DocValues 快速获取每个文档的排序键值,无需加载整个文档。
    • 聚合/分组 (Faceting/Aggregations):进行聚合计算时(如计算平均价格、按品牌分组统计数量),DocValues 提供了对字段值的快速、连续访问能力,非常高效。
    • 脚本访问字段值:在查询或打分脚本中需要获取某个字段的值时,从 DocValues 读取是最高效的方式。
  • 查询性能特点:

    • 查询(过滤)性能极差:如果直接用 DocValues 来做过滤(例如,查找 price = 10.0 的文档),Lucene 必须遍历所有文档或匹配查询的文档子集,对每个文档都从 DocValues 中读取其值,然后进行比较。这是一个线性的扫描过程(O(N)),非常缓慢,尤其是当文档数量巨大时。
    • 排序和聚合性能极好:因为它利用了操作系统的文件系统缓存(OS Cache)。数据是按列连续存储的,访问局部性非常好,可以一次性将整个字段的 DocValues 加载到内存中,后续操作极快。
  • 典型场景: sort, aggregations, script_fields (in Elasticsearch)


性能对比总结表

特性 / 类型倒排索引 (Inverted Index)数字 (Points)DocValues
核心思想Term -> Docs (反向映射)多维空间分割树DocID -> Value (正向映射/列存)
数据结构FST + Postings ListBKD Tree列式存储
最擅长的查询文本搜索、关键词精确匹配数值/日期/地理位置的范围过滤排序、聚合、脚本访问字段值
查询性能Term 越稀有越快对范围大小不敏感,对数级复杂度,非常快用于过滤时性能极差(线性扫描)
不擅长的场景数值范围查询、排序、聚合文本搜索任何形式的搜索/过滤
典型查询TermQuery, BooleanQueryPointRangeQuery不用于查询,用于 sortaggs
磁盘占用相对较大,尤其包含位置信息时相对紧凑相对紧凑,压缩率高
内存占用词典(FST)常驻内存,Postings按需加载索引的内部节点常驻内存,叶子节点按需加载严重依赖 OS Cache,按列加载,对 JVM 堆内存友好

场景化性能分析

假设我们有一个包含商品信息的索引,字段有 description (text), price (float), category (keyword)。

场景一:查找所有描述中包含 “durable” 的商品

  • 最佳选择: 倒排索引
  • 性能分析: Lucene 在 description 字段的词典中找到 “durable”,然后获取其倒排列表,瞬间得到所有匹配的文档 ID。这是它的核心优势。
  • 其他两者: Points 和 DocValues 根本无法完成这个任务。

场景二:查找价格在 100 到 200 之间的所有商品

  • 最佳选择: 数字 (Points)
  • 性能分析: Lucene 在 price 字段的 BKD 树上执行范围查询,高效地剪枝,快速找出所有 price 在 [100, 200] 区间的文档。性能稳定且快速。
  • 其他两者:
    • 倒排索引: 如果强行用倒排索引,会转换成一个超长的 BooleanQuery (OR price:100.0 OR price:100.01 …),性能灾难。
    • DocValues: 需要扫描所有文档,获取每个文档的 price,再判断是否在范围内。性能极差。

场景三:按价格从高到低对搜索结果进行排序

  • 最佳选择: DocValues
  • 性能分析: 假设一个查询已经找到了1000个匹配的文档。为了排序,Lucene 需要知道这1000个文档各自的 price。通过 price 字段的 DocValues,它可以快速、顺序地读取这1000个文档的价格值,然后进行排序。这个过程非常高效,对 JVM 堆内存压力小。
  • 其他两者:
    • 倒排索引: 无法完成排序。
    • Points: BKD 树的设计不是为了高效地按 DocID 随机访问值的,无法用于排序。

场景四:统计每个分类 (category) 下有多少商品(聚合)

  • 最佳选择: DocValues
  • 性能分析: Lucene 遍历所有匹配文档。对于每个文档,通过 category 字段的 DocValues 快速获取其分类值(通常是编码后的数字),然后在一个哈希表中为该分类的计数器加一。由于 DocValues 的列式访问特性,这个过程极快。
  • 其他两者: 倒排索引和 Points 都不适合这个任务。

结论

在 Lucene 8.7.0 (以及现代所有版本) 中,这三种结构协同工作,缺一不可:

  1. 使用倒排索引和 Points 来“查找”和“过滤”文档集。这是查询的第一步,目标是尽可能快地缩小文档范围。
  2. 使用 DocValues 对“找到的”文档集进行“操作”,如排序、聚合或在脚本中提取值。

一个典型的复杂查询(如在电商网站上搜索)会同时利用到它们:

// 伪代码,类似 Elasticsearch 的查询
{"query": {"bool": {"must": [{ "match": { "description": "durable" } } // 使用倒排索引],"filter": [{ "range": { "price": { "gte": 100, "lte": 200 } } } // 使用 Points]}},"sort": [{ "price": "desc" } // 使用 DocValues],"aggs": {"categories": {"terms": { "field": "category" } // 使用 DocValues}}
}

理解它们的根本区别和适用场景,是进行 Lucene/Elasticsearch 性能优化的关键。

http://www.dtcms.com/a/418473.html

相关文章:

  • 关于npm和pnpm
  • Django 中的元类(Metaclass)应用及生产场景示例
  • 以涡度通量塔的高频观测数据为例,基于MATLAB开展;生态碳汇涡度相关监测与通量数据分析实践技术应用
  • 慈溪网站建设哪家好襄阳蒂凯网络网站建设小程序
  • 做网站保存什么格式最好建设银行企业网上银行网站打不开
  • 数据仓库和商务智能考试考点及关系梳理
  • 灵犀互娱笔试
  • 【多线程】什么是原子操作(Atomic Operation)?
  • Visual Studio Code 的 AI 插件汇总
  • Java学习笔记六(集合)
  • 简易分析慢 SQL 的流程和方法
  • Docker 中删除镜像与容器的完整指南
  • 通州手机网站建设北京网站设计实力乐云践新
  • 高速PCB板DDR5数据信号的长STUB要背钻吗?
  • FPGA强化-简易频率计
  • VBScript自动化打印:智能过滤Word文档
  • 解码数据结构内核链表
  • 郑州设计网站公司东莞建设银行电话号码
  • 个人网站的建设目标绥德网站建设
  • Elasticsearch面试精讲 Day 23:安全认证与权限控制
  • 学习嵌入式的第四十三天——ARM——I2C
  • 玳瑁的嵌入式日记---0928(ARM--UART)
  • CentOS 8 部署 Zabbix 7.0 LTS 完整流程(PostgreSQL)及不同系统agent安装
  • 网站开发环境设计怎么在百度做网站推广
  • Langchain+Neo4j+Agent 的结合案例-电商销售
  • 计算机网络【第二章-物理层】
  • 计算机网络【第一章-计算机网络体系结构】
  • mac 安装npm之后,其他终端无法访问
  • 购物网站哪个最好记账公司如何拉客户
  • 汽车网络安全 CyberSecurity ISO/SAE 21434 测试之四