索引完全指南:倒排索引存储格式、分词器体系与两阶段训练管线)
Lance 全文搜索FTS索引完全指南倒排索引存储格式、分词器体系与两阶段训练管线【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance全文搜索索引Full Text Search Index简称 FTS又称倒排索引 inverted index是 Lance 标量索引家族中专门服务于文本检索的一类索引它把词项term映射到包含该词项的文档从而在亿级文本行上实现毫秒级的关键词检索。本文以 docs/src/format/index/scalar/fts.md 为主线结合 rust/lance-index/src/scalar/inverted 下的实际实现完整讲解 FTS 索引的磁盘文件布局与 Schema、InvertedIndexParams全部配置参数、simple/whitespace/raw/ngram/ICU/Jieba/Lindera 分词器、text 与 json 两类文档的 token 化规则、两阶段训练管线及内存调优、分布式训练以及match/phrase/boolean/multi_match/boost等加速查询语法。读完本文你可以独立完成 FTS 索引的创建、参数调优、查询与故障排查。FTS 索引是什么FTS 索引的核心思想是倒排为每个出现在文档中的词项建立一条倒排列表posting list记录哪些文档包含该词项以及出现频次。查询时只需在词项字典中定位查询词再扫描其倒排列表即可而无需逐行扫描原始列。在 Lance 中FTS 索引专为高性能文本检索设计支持多种打分算法如 BM25与短语查询phrase query。索引的详细参数通过 protobuf 消息InvertedIndexDetails持久化对应代码位于 rust/lance-index/src/scalar/inverted/index/inverted_index.rs其中通过pbold::InvertedIndexDetails::try_from(params)完成与 proto 消息的互转训练与查询阶段的参数则统一收敛到 rust/lance-index/src/scalar/inverted/tokenizer.rs 中定义的InvertedIndexParams结构体。存储布局四类文件与分区一个 FTS 索引由多组文件构成分别存放词项字典、文档信息和倒排列表文件作用tokens.lance词项字典把 token 字符串映射为 token IDdocs.lance文档元数据包括每个文档的 token 计数invert.lance每个 token 的压缩倒排列表metadata.lance索引元数据与配置JSON 序列化索引可能包含多个分区partition。每个分区拥有自己独立的一套 token、文档与倒排列表文件文件名以分区 ID 为前缀例如part_0_tokens.lance、part_0_docs.lance、part_0_invert.lance。metadata.lance中列出了索引包含的全部分区 ID。查询时每个分区都必须被搜索结果合并后产生最终排序输出。因此分区数越少查询性能通常越好——每个分区都需要一次独立的词项字典查找和倒排列表扫描。分区数量由训练配置控制核心是环境变量LANCE_FTS_TARGET_SIZE它决定每个合并后的分区可以长到多大详见训练过程。Token 字典文件 Schematokens.lance列类型可空说明_tokenUtf8否token 字符串_token_idUInt32否token 的唯一标识文档文件 Schemadocs.lance列类型可空说明_rowidUInt64否文档行 ID_num_tokensUInt32否文档中的 token 数量分区的docs.lance文件还支持可选的 schema metadata 键total_tokens其值为该文件内_num_tokens之和十进制 UInt64。读取端利用该元数据构造精确的语料统计而无需扫描整个列当键缺失时则回退为对_num_tokens求和。写入端在同一文件提交中依据同一张文档表生成该键。若该键存在但无法解析或与随后加载的_num_tokens求和结果不一致则视为文件损坏。该键的常量定义与一致性校验可在 rust/lance-index/src/scalar/inverted/documents.rsTOTAL_TOKENS_KEY: str total_tokens中看到。倒排列表文件 Schemainvert.lance列类型可空说明_postingListLargeBinary否压缩倒排列表delta 编码的行 ID 与频次_max_scoreFloat32否该 token 的最大得分用于查询优化_lengthUInt32否包含该 token 的文档数量_compressed_positionListListLargeBinary是可选的压缩位置列表用于短语查询倒排列表文件的 schema metadata 中包含posting_block_size每个压缩倒排块编码的文档数量。缺少该元数据的旧索引按传统块大小128读取。block_size参数见下节最终会写入该元数据并被构建器读取例如 rust/lance-index/src/scalar/inverted/builder.rs 中self.params.block_size被用于初始化写入器。元数据文件 Schemametadata.lance元数据文件是 JSON 序列化的配置与分区信息包含两个键键类型说明partitionsArrayUInt64分区 ID 列表用于分布式索引组织paramsJSON Object序列化的 InvertedIndexParams含 tokenizer 配置文件名常量metadata.lance定义于 rust/lance-index/src/scalar/inverted/index/format.rspub const METADATA_FILE: str metadata.lance。InvertedIndexParams全部配置参数params中的InvertedIndexParams是 FTS 索引行为的总开关字段定义见 rust/lance-index/src/scalar/inverted/tokenizer.rs 第 105 行起的结构体字段类型默认值说明base_tokenizerStringsimple基础分词器类型见 Tokenizers 一节languageStringEnglish词干提取与停用词所用语言with_positionBooleanfalse是否存储词项位置以支持短语查询显著增大索引体积max_token_lengthUInt32?None最大 token 长度超过则被移除lower_caseBooleantrue是否将 token 转为小写stemBooleanfalse是否应用语言相关的词干提取stemmingremove_stop_wordsBooleanfalse是否移除指定语言的常见停用词ascii_foldingBooleantrue是否将带重音字符转换为 ASCII 等价形式min_gramUInt322最小 n-gram 长度仅 ngram 分词器max_gramUInt3215最大 n-gram 长度仅 ngram 分词器prefix_onlyBooleanfalse是否只生成前缀 n-gram仅 ngram 分词器block_sizeUInt32128每个压缩倒排块编码的文档数只能为 128 或 256旧索引缺失时按 128 读取256仍属实验性可能引入破坏性变更这些参数在构建索引时被逐一应用到 tokenizer 流水线上base_tokenizer决定词项如何切分language只在stem或remove_stop_words为真时生效with_position决定是否写入_compressed_position列对应源码中InvertedIndexBuilder的with_position字段与写入器初始化逻辑见 builder.rs。分词器体系FTS 索引针对不同文本处理需求提供了多套分词器。基础分词器负责把文本切分为词项随后可按顺序叠加 token 过滤器。基础分词器一览分词器说明适用场景simple按空白与标点切分移除非字母数字字符通用文本默认whitespace仅按空白字符切分需要保留标点raw不切分整个文本作为一个 token精确匹配ngram切分为重叠的字符序列子串/模糊搜索icuICU 基于字典的 Unicode 词切分混合语言文本icu/splitICU 切分后再次按 simple 风格分隔符切分混合语言标识符jieba/*中文分词词级切分中文文本lindera/*日语形态分析分词日文文本以上取值及code代码感知分词需配合analyzercode在 tokenizer.rs 的build_base_tokenizer()中有完整的match分支实现。ICU 分词器混合语言文本ICU 分词器使用 Unicode 词边界规则并对复杂文字采用基于字典的切分。对于默认simple分词器会把手写 CJK 连续串当成一个巨大 token 的混合语言文本ICU 尤为适用。默认情况下Lance 原样保留 ICU 返回的词段使用base_tokenizer: icu/split可对 ICU 词段再次按非字母数字分隔符如下划线与标点切分。例如hello_world こんにちは世界会被切分为hello、world、こんにちは、世界。模型使用随 Lance 打包的编译版 ICU4X segmenter 数据用法指定为icu或icu/split以切分标点分隔的标识符特性Unicode 感知的词边界检测对中文、日文、高棉文、老挝文、缅甸文、泰文进行基于字典的切分无需下载外部语言模型Jieba 分词器中文Jieba 是流行的中文分词库采用基于字典加统计方法的词切分。配置模型目录中的config.json文件模型需下载后放入 Lance 主目录下的jieba/目录用法指定为jieba/model_name或直接用jieba使用默认模型配置结构{ main: path/to/main/dictionary, users: [path/to/user/dict1, path/to/user/dict2] }特性对简体与繁体中文的准确词切分支持自定义用户词典支持多种切分模式精确、全模式、搜索引擎模式Lindera 分词器日文Lindera 是专为日语设计的形态分析分词器解决日语没有词间空格、需要正确分词的问题。配置模型目录中的config.yml文件模型需下载后放入 Lance 主目录下的lindera/目录用法指定为lindera/model_name其中model_name是包含模型文件的子目录名特性带词性标注的形态分析基于字典的分词支持自定义用户词典Token 过滤器过滤器在基础分词器之后按顺序应用过滤器说明配置项RemoveLong移除超过max_token_length的 tokenmax_token_lengthLowerCase转为小写lower_case默认 trueStemmer还原词根stem、languageStopWords移除 the、is、at 等常见词remove_stop_words、languageAsciiFolding重音字符转 ASCIIascii_folding默认 true在源码中这些过滤器由 tantivy 的TextAnalyzer流水线组装见 tokenizer.rs 中build_base_tokenizer()对LowerCaser、AsciiFoldingFilter、stemmer、stop words 的组合。支持的语言用于词干提取与停用词移除的语言包括Arabic、Danish、Dutch、English、Finnish、French、German、Greek、Hungarian、Italian、Norwegian、Portuguese、Romanian、Russian、Spanish、Swedish、Tamil、Turkish。文档类型text 与 jsonLance 支持两类文档text与json。不同文档类型采用不同的 token 化规则解析出的 token 格式也不同。Text 类型Text 类型包括纯文本与文本列表token 由base_tokenizer生成。例如句子Tom lives in San Francisco.被解析为以下 tokenTom lives in San FranciscoJson 类型Json 是嵌套结构Lance 会把 json 文档拆解为path,type,value三元组triplet形式的 token。合法类型为str、number、bool、null。当三元组的 value 是字符串时该文本值会进一步用base_tokenizer切分产生多个三元组 token。查询时Json Tokenizer 使用三元组格式而非原始 json 格式从而简化查询语法。例如给定如下 json 文档{ name: Lance, legal.age: 30, address: { city: San Francisco, zip:us: 94102 } }解析后得到以下 tokenname,str,Lance legal.age,number,30 address.city,str,San address.city,str,Francisco address.zip:us,number,94102随后以三元组格式进行全文检索。要搜索 San Francisco可用以下任一三元组查询address.city:San Francisco address.city:San address.city:Francisco训练过程两阶段管线与调优构建 FTS 索引是一条多阶段流水线扫描源列 → 并行 token 化文档 → 中间结果溢出spill到磁盘 part 文件 → 将 part 文件合并为最终输出分区。对应实现位于 rust/lance-index/src/scalar/inverted/builder.rs 的InvertedIndexBuilder。Phase 1Tokenizationtoken 化输入列以 record batch 流读取并分发给一池 tokenizer 工作任务。每个 worker 独立完成文档 token 化在内存中累积 token、倒排列表与文档元数据。当某个 worker 累积的数据达到分区大小上限或文档数触及u32::MAX时它会将数据以一组 part 文件刷写到磁盘part_id_tokens.lance、part_id_invert.lance、part_id_docs.lance。若单个 worker 处理的数据量足够大它可能产出多个 part 文件。Phase 2Merge合并所有 worker 结束后part 文件被合并为输出分区。part 文件以有界缓冲bounded buffering流式加载避免一次性载入全部数据。对每个 part 文件统一 token 字典、拼接文档集合、以调整后的 ID 重写倒排列表。当一个合并分区达到目标大小后即写入目标存储并开启新分区。所有 part 文件消费完毕后冲刷最后一个分区并写出metadata.lance其中列出分区 ID 与索引参数。配置环境变量环境变量默认值说明LANCE_FTS_NUM_SHARDS计算密集型 CPU 数量并行 tokenizer worker 任务数。越大索引吞吐越高但内存占用越多LANCE_FTS_PARTITION_SIZE256MiBworker 内存缓冲在溢出为 part 文件前的最大未压缩大小LANCE_FTS_TARGET_SIZE4096MiB合并输出分区的目标未压缩大小。更少更大的分区利于查询性能这三个变量在 builder.rs 中均有对应的LazyLock静态定义如LANCE_FTS_NUM_SHARDS、LANCE_FTS_PARTITION_SIZE构建器通过resolve_num_workers()与resolve_worker_memory_limit_bytes()将其解析为实际 worker 数与每 worker 内存上限。此外仓库中还提供了更多进阶环境变量可供排查与调优例如LANCE_FTS_WRITE_QUEUE_SIZE、LANCE_FTS_POSTING_BATCH_ROWS、查询侧LANCE_FTS_SEARCH_CHUNK见 format.rs、LANCE_FTS_POSTING_GROUP_MAX_TOKENS见 prewarm.rs以及LANCE_FTS_REUSE_PREPARED_SCORER见 search.rs。内存与性能考量内存占用主要由两个因素决定LANCE_FTS_NUM_SHARDS——每个 worker 持有独立的进程内缓冲。峰值内存约为NUM_SHARDS * PARTITION_SIZE再加上 token 字典与倒排列表结构的开销。LANCE_FTS_PARTITION_SIZE——取值越大part 文件越少合并阶段代价越低取值越小单 worker 内存越低但会产出更多 part 文件。合并阶段的内存被流式方案所约束part 文件逐个加载仅带少量并发缓冲合并分区的进程内大小受LANCE_FTS_TARGET_SIZE限制。构建 FTS 索引还需要临时磁盘空间来存放 token 化阶段生成的 part 文件。临时空间大小高度依赖是否启用位置信息with_position: true时每个 token 在每篇文档中的每次出现都要记录位置临时磁盘空间很容易达到原列体积的 10 倍以上不带位置的索引通常比原列更小总磁盘空间一般不超过原列体积的 2 倍。性能建议LANCE_FTS_TARGET_SIZE越大输出分区越少对查询越有利因为查询必须扫描每个分区的 token 字典。内存允许时优先选择更少、更大的分区。with_position: true会为每一次出现存储词项位置显著增大索引体积仅在需要短语查询时开启。ngram 分词器相比词级分词器为每篇文档生成多得多的 token索引体积与内存占用都会明显增大。分布式训练FTS 索引支持分布式训练不同 worker 节点各自索引数据的一个子集之后由协调节点汇总结果。这一流程与 builder.rs 中InvertedIndexBuilder::new_with_fragment_mask的实现一一对应每个分布式 worker 被分配一个fragment mask(fragment_id as u64) 32并 OR 进它生成的分区 ID 中从而保证跨 worker 的分区 ID 全局唯一。worker 设置skip_merge: true直接写出各自的 part 文件不执行合并阶段。不再写出单一的metadata.lance每个 worker 改而写出按分区命名的元数据文件part_id_metadata.lance。所有 worker 完成后协调节点合并元数据文件收集全部分区 ID将它们重映射为从 0 开始的连续序列同时重命名对应的数据文件并写出最终统一的metadata.lance。这种设计让每个 worker 在 token 化阶段完全独立工作只有最后的元数据合并需要单节点步骤而它只是重命名文件与写一个小元数据文件非常轻量。相关函数包括write_part_metadata、part_metadata_file_path与元数据合并逻辑均在 builder.rs 中对应的测试覆盖可见 rust/lance-index/src/scalar/inverted/index/tests/format_and_builder.rs其中构造了fragment_mask 7_u64 32及skip_merge: true的用例。加速查询 API 与查询类型Lance SDK 提供了专用的全文搜索 API 来发挥 FTS 索引能力支持远超简单 token 匹配的复杂查询类型。Python 端入口为 python/python/lance/dataset.py 中ScannerBuilder.full_text_search(query, columnsNone)传入字符串时执行 match 查询传入FullTextQuery对象时可表达所有下述复杂查询且此时忽略columns参数。查询 JSON 的解析逻辑match/phrase/boost/multi_match/boolean分派位于 rust/lance-index/src/scalar/inverted/parser.rs。查询类型说明示例用法结果类型contains_tokens基于 token 的基础搜索UDF使用 BM25 打分并自动排序结果SQLcontains_tokens(column, search terms)AtMostmatch可配置 AND/OR 运算符与相关性打分的匹配查询{match: {query: text, operator: and/or}}AtMostphrase基于位置信息的精确短语匹配要求with_position: true{phrase: {query: exact phrase}}AtMostboolean含 must/should/must_not 子句的复杂布尔查询{boolean: {must: [...], should: [...]}}AtMostmulti_match跨多字段统一打分的搜索{multi_match: [{field1: query}, ...]}AtMostboost按可配置因子提升特定词项或查询的相关性得分{boost: {query: {...}, factor: 2.0}}AtMost索引创建与运维提示在执行搜索前必须先在目标列上创建倒排索引。Python 侧通过Dataset.create_index(column, index_typeINVERTED, with_position..., replaceTrue)完成python/python/lance/dataset.py 第 4247 行起with_position参数对应上文InvertedIndexParams.with_position开启后支持短语查询。若开启短语查询并希望避免冷启动时的位置数据延迟加载可使用prewarm_index(name, with_positionTrue)在预热阶段一并把位置数据载入索引缓存见 python/python/lance/dataset.py 第 4681 行起的实现。查询前可先通过索引元数据查看分区数分区数越少受LANCE_FTS_TARGET_SIZE控制每次查询需要做的 token 字典查找与倒排列表扫描越少。小结Lance 的 FTS 索引通过tokens.lance、docs.lance、invert.lance、metadata.lance四类文件实现了完整的倒排存储词项字典、文档统计含total_tokens元数据校验、压缩倒排列表含posting_block_size兼容策略与 JSON 参数InvertedIndexParams分层清晰。分词层面覆盖通用simple/whitespace/raw、子串ngram、多语言ICU、中文Jieba与日文Lindera并支持 18 种语言的词干与停用词处理。训练侧的两阶段管线以LANCE_FTS_NUM_SHARDS、LANCE_FTS_PARTITION_SIZE、LANCE_FTS_TARGET_SIZE三个旋钮调节吞吐、内存与查询性能fragment mask 与 per-partition metadata 机制则让大规模分布式建索引成为可能。最后match、phrase、boolean、multi_match、boost等查询类型为上层应用提供了从简单关键词到复杂布尔检索的完整能力矩阵。更多标量索引如 ngram、btree、zonemap、bloom_filter 等可继续阅读 docs/src/format/index/scalar 目录下的对应文档。【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考