Lucene底层原理:倒排索引实现原理与代码实战,彻底吃透搜索引擎核心
-
- 前言
- 一、什么是倒排索引?
-
- [1.1 正排索引(数据库索引)](#1.1 正排索引(数据库索引))
- [1.2 倒排索引(搜索引擎索引)](#1.2 倒排索引(搜索引擎索引))
- [1.3 核心结构](#1.3 核心结构)
- 二、倒排索引完整结构
-
- [2.1 示例](#2.1 示例)
- [三、Lucene 倒排索引构建完整流程(底层真实流程)](#三、Lucene 倒排索引构建完整流程(底层真实流程))
-
- [3.1 构建步骤(图文版)](#3.1 构建步骤(图文版))
- [3.2 构建流程图](#3.2 构建流程图)
- [四、Lucene 倒排索引检索流程](#四、Lucene 倒排索引检索流程)
-
- [4.1 查询流程](#4.1 查询流程)
- [4.2 为什么这么快?](#4.2 为什么这么快?)
- [五、手写实现:极简版倒排索引(Java 代码)](#五、手写实现:极简版倒排索引(Java 代码))
-
- [5.1 代码实现](#5.1 代码实现)
- [5.2 运行结果](#5.2 运行结果)
- [六、Lucene 倒排索引真实底层存储格式](#六、Lucene 倒排索引真实底层存储格式)
- [七、Lucene 倒排索引的核心优化(ES 高性能的秘密)](#七、Lucene 倒排索引的核心优化(ES 高性能的秘密))
-
- [7.1 Term Index (基于 FST 结构)](#7.1 Term Index (基于 FST 结构))
- [7.2 Posting List 压缩算法](#7.2 Posting List 压缩算法)
- [7.3 有序倒排表](#7.3 有序倒排表)
- [7.4 段(Segment)不可变](#7.4 段(Segment)不可变)
- 八、倒排索引核心总结(面试必背)
- 九、本文总结
|-----------------------------|
| 🌺The Begin🌺点点关注,收藏不迷路🌺 |
前言
倒排索引(Inverted Index) 是 Lucene 和 Elasticsearch 的灵魂 ,是全文检索能做到秒级响应的核心数据结构。
几乎所有搜索引擎、大数据检索组件,底层都依赖倒排索引。但绝大多数开发者只知其名,不知其实现。
本文从原理 → 结构 → 构建流程 → 代码实现 → 检索流程,用最通俗的方式带你从零实现 Lucene 倒排索引,彻底搞懂 ES 为什么快。
一、什么是倒排索引?
1.1 正排索引(数据库索引)
文档ID → 单词列表
需要遍历所有文档才能查关键词,慢。
1.2 倒排索引(搜索引擎索引)
单词 → 文档ID列表(倒排表)
通过关键词直接定位文档,极快。
1.3 核心结构
- Term(词项):分词后的最小单元(关键词)
- Posting List(倒排表):包含这个词的文档ID集合
- Term Dictionary(词词典):Term 的排序集合
- Term Index(词项索引):对 Term Dictionary 的索引,加速查找
二、倒排索引完整结构
Term Index (单词索引)
↓
Term Dictionary (单词词典:排序、二分查找)
↓
Posting List (倒排表:文档ID列表、频率、位置)
2.1 示例
文档:
1:我爱Java
2:Java编程
3:编程学习
倒排索引:
Java → [1, 2]
编程 → [2, 3]
我爱 → [1]
学习 → [3]
三、Lucene 倒排索引构建完整流程(底层真实流程)
3.1 构建步骤(图文版)
- 文档采集:读取原始文档内容
- 分词(Analyzer):将文本切分成 Term
- 词项处理:转小写、去停用词、归一化
- 建立映射:Term → 文档ID、词频、位置
- 写入内存缓冲区
- 生成段文件(Segment)
- 持久化到磁盘
3.2 构建流程图
#mermaid-svg-Lz1hK2vfnpF4Rex4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .error-icon{fill:#552222;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .marker.cross{stroke:#333333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 p{margin:0;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster-label text{fill:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster-label span{color:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster-label span p{background-color:transparent;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .label text,#mermaid-svg-Lz1hK2vfnpF4Rex4 span{fill:#333;color:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .node rect,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node circle,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node ellipse,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node polygon,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .rough-node .label text,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node .label text,#mermaid-svg-Lz1hK2vfnpF4Rex4 .image-shape .label,#mermaid-svg-Lz1hK2vfnpF4Rex4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .rough-node .label,#mermaid-svg-Lz1hK2vfnpF4Rex4 .node .label,#mermaid-svg-Lz1hK2vfnpF4Rex4 .image-shape .label,#mermaid-svg-Lz1hK2vfnpF4Rex4 .icon-shape .label{text-align:center;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .node.clickable{cursor:pointer;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .arrowheadPath{fill:#333333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Lz1hK2vfnpF4Rex4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lz1hK2vfnpF4Rex4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster text{fill:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .cluster span{color:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Lz1hK2vfnpF4Rex4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .icon-shape,#mermaid-svg-Lz1hK2vfnpF4Rex4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .icon-shape p,#mermaid-svg-Lz1hK2vfnpF4Rex4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .icon-shape .label rect,#mermaid-svg-Lz1hK2vfnpF4Rex4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lz1hK2vfnpF4Rex4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lz1hK2vfnpF4Rex4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lz1hK2vfnpF4Rex4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始文档
分词器Analyzer
生成Term词项
建立Term→DocID映射
写入内存缓冲区
生成倒排索引段Segment
写入磁盘
可检索
四、Lucene 倒排索引检索流程
4.1 查询流程
- 输入查询关键词
- 分词生成 Term
- 通过 Term Index 快速定位
- 在 Term Dictionary 二分查找
- 获取 Posting List
- 取文档ID → 返回结果
4.2 为什么这么快?
- Term Index 放在内存,O(1) 定位
- Term Dictionary 有序,二分查找 O(logN)
- Posting List 压缩存储,IO 极小
五、手写实现:极简版倒排索引(Java 代码)
下面用 100 行 Java 代码 实现一个迷你 Lucene 倒排索引,包含:
- 分词
- 索引构建
- 关键词检索
5.1 代码实现
java
import java.util.*;
/**
* 极简倒排索引实现
*/
public class InvertedIndex {
// 倒排索引核心结构:Term -> 文档ID集合
private final Map<String, Set<Integer>> index = new HashMap<>();
// 新增文档,构建索引
public void addDocument(int docId, String content) {
// 1. 分词(简单按空格分词)
String[] terms = content.split(" ");
for (String term : terms) {
term = term.toLowerCase(); // 统一小写
// 2. 创建倒排项
index.computeIfAbsent(term, k -> new HashSet<>()).add(docId);
}
}
// 关键词检索
public Set<Integer> search(String keyword) {
return index.getOrDefault(keyword.toLowerCase(), Collections.emptySet());
}
// 测试
public static void main(String[] args) {
InvertedIndex index = new InvertedIndex();
// 添加文档
index.addDocument(1, "I love Java");
index.addDocument(2, "Java programming");
index.addDocument(3, "programming study");
// 查询
System.out.println(index.search("Java")); // [1,2]
System.out.println(index.search("programming"));// [2,3]
}
}
5.2 运行结果
[1, 2]
[2, 3]
这就是 Lucene 倒排索引最核心的原理!
六、Lucene 倒排索引真实底层存储格式
Lucene 会把倒排索引存储为 .tim、.tip、.doc、.pos 等文件:
| 文件 | 作用 |
|---|---|
.tip |
Term Index(内存索引) |
.tim |
Term Dictionary(词词典) |
.doc |
Posting List(文档ID列表) |
.pos |
词项位置 |
.pay |
有效载荷 |
七、Lucene 倒排索引的核心优化(ES 高性能的秘密)
7.1 Term Index (基于 FST 结构)
- 内存占用极小
- 极高检索效率
- 支持前缀匹配
7.2 Posting List 压缩算法
- FOR 压缩
- PFOR 压缩
- 空间减少 80%+
7.3 有序倒排表
- 快速求交、合并、求并
- 加速多条件查询
7.4 段(Segment)不可变
- 无锁
- 高并发
- 检索极快
八、倒排索引核心总结(面试必背)
- 倒排索引 = Term + Term Dictionary + Posting List
- Lucene 使用 FST 构建 Term Index
- Posting List 存储文档ID、词频、位置
- 查询 = 词项查找 + 倒排表取文档
- 段文件不可变,高性能基石
九、本文总结
倒排索引是搜索引擎的核心,Lucene 作为 ES 底层,通过:
- 分词
- 倒排映射
- FST 索引
- 压缩存储
- 段不可变
实现了海量数据下的毫秒级检索。
理解倒排索引,你就真正理解了 Elasticsearch 为什么是世界上最快的搜索引擎。

|---------------------------|
| 🌺The End🌺点点关注,收藏不迷路🌺 |