全文索引能力

最近更新时间:2026-01-06 10:48:01

我的收藏

背景

TSearch 的全文索引能力基于列存系统构建,突破了传统列存以块级索引为主的局限,实现了行级倒排索引,显著提升了明细数据场景下的检索效率和精度。倒排链结构参考并优化了 Lucene 最新版本,移除了评分机制,仅保留必要的倒排信息,从而降低索引体积。
目前,TSearch 全文索引支持两种短语查询模式:
1. 精简模式:不存储词位置信息,通过 Match 查询结合后过滤逻辑实现短语匹配,节省存储空间。
2. 传统模式:存储词位置信息,以空间换取更高的检索性能。
TSearch 提供丰富的分词策略,并兼容大部分 Elasticsearch 的 Term Query 和 Full Text 查询语法,降低了用户迁移成本。全文索引在系统中作为加速手段而非必选依赖,即使没有索引,系统仍能完成全文检索,并根据是否存在索引自动选择最优查询计划。

数据结构

TSearch 的倒排索引存储了某个单词在一个文档或一组文档中的位置映射,这些位置的链表也称为倒排链表(posting list)。在单词查询或精简模式的短语查询场景中,倒排索引仅包含每个引用单词所在文档的列表;而在传统模式的短语查询场景中,倒排索引不仅包含每个引用单词所在文档的列表,还记录每个单词在文档中的具体位置。
TSearch倒排索引的结构由两个缓冲区组成:size_buffer用于存储倒排链中文档 ID 的数量和倒排链的长度;data_buffer用于存储编码后的倒排链数据。其中,倒排链的长度可以用于跳过某个未被使用的词项的整个倒排链。
在存储倒排链数据的 data_buffer 中,数据以递增的文档 ID 差值形式存储,并通过 PFOR 算法进行分块压缩。倒排链表无需完全解压即可使用,可以以块为单位局部解码倒排链数据。doc_buffer 用于存储解码后的文档 ID。当 doc_buffer 中表示当前文档 ID 的游标到达缓冲区末尾时,从 data_buffer 中读取足够包含一个压缩块的数据,并使用 PFOR 算法进行解码,将解码后的数据写入 doc_buffer。如果 data_buffer 中剩余数据不足,则对缓冲区进行填充;若剩余数据量不足以组成一个完整压缩块(通常为 128 个数),则调用特殊的 PFOR 算法对尾部数据进行解压。

读取倒排链中的文档ID
读取倒排链中的文档ID

链表中的文档 ID 是递增的,因此在多链表合并时,只需局部解压多个倒排链表的压缩数据流可完成合并。同时读取多个倒排链头部文档ID,维护一个小根堆进行多倒排链合并。
倒排链表合并
倒排链表合并

倒排链的构建逻辑也类似。对于 PostingListDataForAdd::add(docID)的调用累计达到128次时,会对这128个文档ID进行 PFOR 编码,并写入一个 PostingListBlock。多个 PostingListBlock 被串联成链表。遍历 PostingListBlock 链表时,将每个块中的文档ID压缩数据写入 writeBuffer,最后的尾部文档ID也会经过 PFOR 编码后写入 writeBuffer


编码设计

对于倒排链表在磁盘上的存储,我们采用 PFOR 编码来存储文档ID的差值。TSearch主要结合位打包块和可变异常值两种方案,并在所有值相同时使用常量块。这种组合方法能够在保持高压缩率的同时适应各种数据分布情况:
1. 位打包块:适用于值的位宽≤32位且变化不大的场景。它存储一个位宽和紧凑的位流,并通过 SIMD 技术进行位打包和解码。
2. 可变异常值:适用于大多数值的位宽较小但少数值较大的情况。数据被分为主流(常见位宽)和异常值两部分进行处理。
3. 常量块:适用于所有值相同的场景。它存储一个≤32位的值和位宽,每个元素均使用该值,压缩率极高。

查询示意图

对于单谓词查询,通过查询倒排索引并合并后得到 posting list,从而确定命中行。对于多谓词查询,会通过查询多个 Posting List 并进行多步 prewhere 优化确定命中行。
单谓词全文索引查询
单谓词全文索引查询

多谓词全文索引查询
多谓词全文索引查询


使用文档(API 文档)

功能说明

TSearch 的倒排索引对特定类型的数据列提供行级索引能力,默认不会开启,可通过字段属性配置。

使用说明

使用 tsearch_indextsearch_index_option字段属性显式指定是否建立倒排索引以及倒排索引的版本。倒排索引默认不开启,倒排索引版本默认值为 inverted_v2(精简倒排索引,稳定版本)。倒排索引版本的默认值可以通过设置进行配置(索引层面,在索引建立时配置,无法更改)。
PUT /index_test
{
"mappings": {
"properties": {
"name": {
"type": "text",
"tsearch_index": true,
"tsearch_index_option": "inverted_v2"
},
"address": {
"properties": {
"road": {
"type": "text",
"tsearch_index": false
}
}
}
}
}
}

默认值与存储规则

tsearch_index 的默认值为 falsetsearch_index_option 的默认值为 inverted_v2,默认值可通过 settings.index.tsearch.index.option.default 配置,且只能在索引创建时设置,创建后无法更改。
tsearch_index 为默认值时,不会进行存储。当创建索引时未指定 tsearch_index_option,不会存储。
tsearch_index_option 当前支持以下类型:inverted, inverted_v1, inverted_v2, inverted_v3
GET /index_test/_mapping
->
{
"index_test" : {
"mappings" : {
"properties" : {
"address" : {
"properties" : {
"road" : {
"type" : "text"
}
}
},
"address" : {
"type" : "text",
"tsearch_index" : "true",
"tsearch_index_option" : "inverted_v2"
}
}
}
}
}
配置版本默认值:
PUT /index_test_with_default_tsearch_index_option
{
"settings": {"index.tsearch.index.option.default":"inverted_v1"},
"mappings": {
"properties": {
"col1": {
"type": "text",
"tsearch_index": true
},
"col2": {
"type": "text",
"tsearch_index": true,
"tsearch_index_option": "inverted"
}
}
}
}

GET /index_test/_mapping
->
{
"index_test_with_default_tsearch_index_option" : {
"mappings" : {
"properties" : {
"col1" : {
"type" : "text",
"tsearch_index" : "true" /// 索引版本与配置过的默认值相同:inverted_v1
},
"col2" : {
"type" : "text",
"tsearch_index" : "true",
"tsearch_index_option" : "inverted"
}
}
}
}
}

分支情况说明

只有text, keyword类型字段支持tsearch_index, tsearch_index_option字段属性。
通过PUT _mapping_bulk动态添加字段时,默认tsearch_indexfalse, tsearch_index_optionsettings.index.tsearch.index.option.default值。
暂不支持显式指定多字段(Multi-field)的tsearch_index/tsearch_index_option 属性, 取默认值。
tsearch_index/tsearch_index_option字段属性设置后不可更改。
tsearch_index_option不在支持范围内,抛出异常。


有无倒排索引对比

使用 http_logs(1.7G)作为数据集,分别对 request 字段建立/不建立倒排索引并查询。对比结果如下:
对比项
有倒排索引
无倒排索引
写入速度(单线程)
73.3M/s
107.6M/s
存储大小
232M
202M
查询耗时
16ms
61ms
建表语句与查询语句如下:
PUT /table_with_inverted

{
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"clientip": {
"type": "text"
},
"request": {
"type": "text"
},
"status": {
"type": "integer"
},
"size": {
"type": "long"
}
}
}
}

PUT /table_no_inverted

{
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"clientip": {
"type": "text"
},
"request": {
"type": "text",
"tsearch_index": true
},
"status": {
"type": "integer"
},
"size": {
"type": "long"
}
}
}
}

GET /table_with_inverted/_search

{"query":{"term":{"request":"ps_bdr_l.gif"}}}