内容提要
TIN 是已正式发布的 Postgres 全文搜索扩展,支持布尔、短语、模糊和通配符查询及 BM25 排序,性能优于 ParadeDB、pg_textsearch 和 GIN。它直接使用 Postgres 的 ctid 作为文档标识,避免 ID 映射,并利用位图和向量指令加速查询;写入性能下降小,合并段时无需重新编号,减少写放大。
延伸解读
TIN 的 ctid 设计如何提升性能
TIN 直接使用 Postgres 的 ctid 作为文档标识,避免了其他扩展(如 ParadeDB 和 pg_textsearch)所需的 ID 映射步骤。当查询匹配大量行时,无需额外查找映射表,减少了 CPU 和 I/O 开销。同时,ctid 的物理顺序特性使得从堆中读取数据时能按堆顺序进行,利用顺序访问优势,进一步提升查询速度。
位图与向量化查询优化
TIN 利用 Postgres 页面的特性,采用两级位图编码:页级位图和页内偏移位图。页级位图恰好 256 位,可放入 AVX2 寄存器,实现向量化的 AND/OR 操作。对于 COUNT 查询,还能使用 POPCNT 指令快速计数。这种设计大幅减少了需要解码的数据量,并避免了昂贵的循环和分支,从而加速查询。
写入与合并的效率优势
由于使用 ctid,TIN 在合并段时无需重新编号文档,位图可以直接重用,减少了写放大和 CPU/I/O 成本。在并发写入场景下,TIN 仍能保持高查询吞吐量,而 ParadeDB 和 pg_textsearch 则出现明显下降。TIN 还通过 liveness 位图和可见性映射优化,确保 MVCC 正确性并减少堆检查。
基准测试结果解读
在 85GB Stack Exchange 语料上,TIN 的查询吞吐量至少是 ParadeDB 的 8 倍,p99 延迟显著更低。对于混合查询,TIN 达到 199 QPS,而 ParadeDB 仅 7.9 QPS。在并发更新下,TIN 仍保持 172 QPS,而 ParadeDB 降至 6.0 QPS。这些结果体现了 TIN 架构在读写混合负载下的综合优势。
Q&A
TIN 是什么?它和 Postgres 自带的全文搜索有什么区别?
TIN 是 Postgres 的全文搜索扩展,全称 Text INdex,已正式发布(GA)。它支持布尔、短语、跨度查询,模糊、通配符和正则匹配,大小写和重音折叠,COUNT(*) 查询以及 BM25 排序的 top-k 查询。与 Postgres 自带的 GIN 索引相比,TIN 性能更高,且能处理 GIN 无法完成的工作负载(如析取查询)。
TIN 的查询性能比 ParadeDB、pg_textsearch 和 GIN 快多少?
根据基准测试,TIN 的吞吐量至少是其他方案的 8 倍。具体来说:混合查询 top-10 时,TIN 的 QPS 是 ParadeDB 的 25 倍,p99 延迟低 26 倍;合取和短语查询时,TIN 的 QPS 是 ParadeDB 的 10 倍、GIN 的 541 倍;析取查询带并发写入时,TIN 的 QPS 是 pg_textsearch 的 36 倍、ParadeDB 的 57 倍。
TIN 为什么比传统文本索引快?
TIN 直接使用 Postgres 的 ctid 作为文档标识,避免了 ID 映射开销。它利用页级和偏移级位图进行压缩,并借助 AVX2/AVX-512 向量指令加速交并操作,使用 POPCNT 指令计数。此外,TIN 在合并段时无需重新编号,减少了写放大。
TIN 如何处理并发写入和 MVCC 可见性?
TIN 支持并发写入,查询能立即看到已提交的新行。它通过堆检查、可见性映射和自定义扫描来保证 MVCC 正确性。对于 COUNT(*) 查询,如果页面全部可见,TIN 可直接返回计数而不访问堆;对于有更新的页面,TIN 将页级位图与可见性映射相交,只检查不可见页面。VACUUM 运行时,TIN 会清除已删除元组的活跃位。
TIN 的索引构建时间和大小如何?
在 85 GB Stack Exchange 语料上,TIN 构建索引耗时 8 分 10 秒,索引大小 50.7 GB,所需内存 32 GB。相比之下,ParadeDB 耗时 19 分 20 秒,pg_textsearch 耗时 26 分 49 秒,Postgres GIN 耗时 2 小时 9 分 4 秒。TIN 在构建时间和内存需求上均优于其他方案。
TIN 支持哪些类型的查询?
TIN 支持布尔表达式(合取、析取)、短语查询、跨度查询,以及模糊、通配符和正则表达式匹配。它还支持大小写和重音折叠,COUNT(*) 查询和 BM25 评分的 top-k 查询。
TIN 如何处理文档更新和删除?
TIN 使用可变段来快速插入新文档,后台工作进程将可变段提升为不可变段。删除通过每个段的活跃位图跟踪,VACUUM 清除已删除元组的位。合并段时,由于使用 ctid 作为标识,无需重新编号,位图可直接转移,减少写放大。