背景
目前,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,维护一个小根堆进行多倒排链合并。
倒排链表合并
倒排链的构建逻辑也类似。对于 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"}}}