背景
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,维护一个小根堆进行多倒排链合并。

倒排链的构建逻辑也类似。对于
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_index和 tsearch_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 的默认值为 false,tsearch_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_index为false, tsearch_index_option为settings.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"}}}