在处理 PB 级海量数据时,为什么传统数据库(如 MySQL)使用 LIKE 模糊查询会慢如蜗牛,而 INFINI Easysearch 却能瞬间返回结果?
这并非魔法,而是源于底层数据结构设计的根本差异。传统数据库是为了“记录”而生,而 Easysearch 是为了“检索”而生。这种差异的核心,就是我们今天要解密的黑科技——倒排索引(Inverted Index)。
本文将带您深入 Easysearch 的引擎盖之下,看看这一核心机制是如何工作的,以及它是如何支撑起企业级的高性能搜索需求的。
一、 正排 vs 倒排:换个视角看数据 #
要理解“倒排”,首先得理解什么是“正排”。
1.1 正排索引(Forward Index) #
想象你手里拿着一本几百页的书。
- 结构:书页(文档 ID) -> 内容(关键词)。
- 场景:如果你想知道“第 10 页讲了什么”,这很快。
- 痛点:如果你想知道“哪几页提到了‘人工智能’这个词”,你就必须从第一页翻到最后一页。这就是传统数据库全表扫描(Full Table Scan)慢的原因。
1.2 倒排索引(Inverted Index) #
现在,请翻到这本书的最后,看看“索引/术语表”。
- 结构:关键词(Term) -> 书页列表(Posting List)。
- 机制:你直接在按字母排序的列表中找到“人工智能”,后面紧跟着页码:
[5, 18, 99, 102]。 - 结果:你瞬间就锁定了目标,无需遍历整本书。
Easysearch 的核心逻辑正是如此:它在数据写入时,就已经把数据“打散”并重新排列,构建了一本巨大的“术语表”。
二、 核心组件:解剖倒排索引 #
在 Easysearch(基于 Lucene 内核)中,倒排索引的结构比简单的“术语表”要精妙得多,主要包含三个核心部分:
|快速定位| TD TD -->|指向| PL
end
style TI fill:#fff5f5,stroke:#e91e63,stroke-width:2px
style TD fill:#f3f4ff,stroke:#3f51b5,stroke-width:2px
style PL fill:#f1f8e9,stroke:#4caf50,stroke-width:2px -->
2.1 Term Dictionary(词项字典) #
这是所有文档中分词后去重的关键词集合。Easysearch 会将它们按字典序排序(a, b, c… 或 一, 二, 三…)并分块存储。
- 存储内容:
- 完整的 term 文本(使用前缀压缩)
- 指向 Posting List 的文件偏移量
- term 的统计信息(文档频率等)
- 组织方式:
- 分成多个 Block(每个 Block 包含 25-48 个 term)
- 每个 Block 内部按字典序排序
- Block 之间也按字典序排列
- 查找方式:
- 通过 Term Index (FST) 快速定位到目标 Block
- 读取该 Block 到内存
- 在 Block 内进行二分查找
- 问题:
当数据量达到十亿级时,完整的 Term Dictionary 可能达到数 GB 甚至数十 GB,
无法全部加载到内存。 - 解决方案:
- Term Index (FST) 常驻内存,体积仅为 Term Dictionary 的约 10%
- Term Dictionary 按 Block 存储在磁盘
- 依赖操作系统文件缓存加速热点数据访问
- 只需读取相关 Block,无需加载全部数据
2.2 Term Index(词项索引) #
为了解决 Term Dictionary 过大无法全部加载到内存的问题,Easysearch 引入了 Term Index。它就像是"字典的目录"。
FST (Finite State Transducer):
这是 Easysearch 实现"秒搜"的关键数据结构。它是一种极度节省内存的有向无环图结构,通过共享公共前缀和后缀来压缩存储空间。
工作原理:
- 存储内容:完整的 term → 磁盘文件偏移量的映射
- 压缩技术:通过状态机复用公共前缀,多个 term 共享相同的状态转移路径
- 示例:
存储 "mop", "moth", "mother", "star", "start" 时:
"mo" 前缀被 "mop", "moth", "mother" 共享
"star" 前缀被 "start" 复用
FST 内部只存储一次 "mo",节省大量空间
查询效果:
- 体积极小:Term Index 通常只有 Term Dictionary 的 10-15%,可以完全常驻内存
- 精确定位:查询时,FST 返回精确的文件偏移量(字节级),直接定位到 Term Dictionary 中目标 term 所在的 Block
- 高效查找:在 Block 内通过二分查找定位到精确的 term
- 时间复杂度:O(term_length),通常只需 10-20 次状态转移
2.3 Posting List(倒排表) #
这是记录包含特定关键词的文档 ID 列表(如 Doc1, Doc3, Doc8)。Easysearch 在这里做了极深度的优化:
丰富的元数据存储:
- 文档 ID:哪些文档包含该 term
- 词频 (TF - Term Frequency):term 在每个文档中出现的次数
- 位置 (Position):term 在文档中的精确位置,用于短语搜索(如 “machine learning”)
- 偏移量 (Offset):term 在原始文本中的字符偏移,用于搜索结果高亮显示
高效的压缩编码:
Posting List 使用 FOR (Frame of Reference) + PForDelta 编码:
// 原始文档 ID: [100, 101, 105, 108, 200, 201]
// 存储为增量: [100, 1, 4, 3, 92, 1]
// 小数字用更少的 bit 存储,压缩比可达 5-10 倍
快速的集合运算:
当进行复杂的组合查询(例如:Easysearch AND 高性能)时,需要对两个 Posting List 求交集。Easysearch 使用以下优化:
- 跳表 (Skip List):
Posting List: [1, 5, 10, 15, 50, 100, ...]
Skip List: [1 ──→ 50 ──→ 100]
查找 doc >= 45 时,直接跳到 50,无需逐个遍历
- 双指针算法:同时遍历两个有序列表,时间复杂度 O(n+m)
- 提前终止:利用 TF-IDF 评分,当剩余文档不可能改变 Top-K 结果时提前结束
- 位集优化:对于高频 term,在内存中使用位图缓存,加速重复查询
三、 Easysearch 的黑科技:如何做到“既快又省”? #
倒排索引虽然快,但会占用大量空间。Easysearch 面向企业级场景,在“快”的同时,还必须解决“省”的问题。
3.1 Frame of Reference (FOR) 编码 #
Easysearch 不会直接存储 [100, 101, 105, 108] 这样的文档 ID 列表,因为整数存储很占空间。
它采用 Delta Encoding(增量编码):
- 原始:
100, 101, 105, 108 - 存储差值:
100, 1, 4, 3
差值通常很小,可以使用更少的 Bit 来存储(Bit Packing),从而大幅压缩索引体积。
3.2 ZSTD 极致压缩 #
除了索引结构本身的压缩,Easysearch 在存储层引入了 ZSTD 压缩算法。
- 对于存储原始数据的
_source字段,ZSTD 提供了比传统 LZ4 高得多的压缩比。 - 价值:这意味着同样的倒排索引机制下,Easysearch 占用的磁盘空间比原生 Elasticsearch 减少 30%-50%,直接降低硬件成本。
3.3 中文分词的优化 #
倒排索引建立的前提是“分词”(Tokenization)。
- 挑战:英文按空格分词很简单,但中文(如“南京市长江大桥”)很复杂。
- Easysearch 优势:内置优化的中文分词器,能够更智能地处理中文语境,避免索引膨胀,同时保证查全率和查准率。
四、 总结:为什么选择 Easysearch? #
通过倒排索引的深度解析,我们可以看到 Easysearch 的性能并非凭空而来,而是建立在坚实的数据结构与算法之上:
- FST 技术:让核心索引常驻内存,实现毫秒级定位。
- 增量编码与位图:让海量数据的交集、并集运算快如闪电。
- ZSTD 压缩:在保证速度的同时,极大降低存储成本。
Easysearch 将这些复杂的底层技术封装在简单易用的 API 之下。对于企业用户而言,您无需理解 FST 或 Roaring Bitmaps 的数学原理,只需接入 Easysearch,即可立即获得 PB 级数据的“秒搜”体验。
无论是日志分析、业务搜索还是 AI 知识库检索,Easysearch 的倒排索引引擎都是您最可靠的性能保障。





