全球数据预计到今年将爆炸性增长到 181 ZB,其中 80% 为非结构化数据。这样的数据洪流让搜索引擎的战场早已不再局限于“找到结果”,而是演变为“精准捕捉每一个意图”。而如何“聪明地”理解意图,让结合全文检索和向量检索的强大混合检索技术成为了 AI 数据库的核心竞争力。那么,基于 ClickHouse 构建的 AI 数据库是如何增强全文检索功能,实现对非结构化文本数据的直观高效访问?答案就是集成全文搜索引擎库 Tantivy

在本文中,我们将为大家分享集成过程的技术细节以及它如何提升 AI 数据库的性能,希望对有类似需求的产品有所助益。

为什么选择 Tantivy

ClickHouse 提供了基本的文本搜索功能,如hasTokenstartsWithmultiSearchAny,适用于简单的词项查询场景。然而,对于更复杂的需求,如短语查询、模糊文本匹配和 BM25 相关性排序,这些功能就显得不够用了。因此,在 AI 数据库中,我们引入了 Tantivy 作为全文索引的底层实现,增强其全文搜索功能。Tantivy 的全文索引支持模糊文本查询、BM25 相关性排序,并加速了现有的hasTokenmultiSearchAny词项匹配功能。同时,Tantivy 是一个用 Rust 编写的开源全文搜索引擎库,专为处理大量文本数据而设计,具有出色的速度和效率。

理解 Tantivy 的核心原理

Tantivy 核心原理
  • 构建索引:Tantivy 对输入文本进行分词,将其分割为独立的词项。然后,它创建一个倒排索引(倒排列表)并将其写入索引文件(段)。与此同时,Tantivy 的后台线程利用合并策略合并和更新这些段索引文件。

  • 执行文本搜索:当用户发起文本搜索查询时,Tantivy 解析查询语句,提取词项,并在每个段上根据查询条件和BM25 相关性算法对文档进行排序和评分。最后,根据相关性得分合并这些段的查询结果,并返回给用户。

Tantivy 的关键特性

  • BM25 相关性评分:Elasticsearch、Lucene 和 Solr 都使用 BM25 作为默认的相关性排序算法。BM25 评分评估文本搜索的准确性和相关性,提升用户的搜索体验。

  • 可配置的分词器:支持各种语言的分词器,满足用户多样化的分词需求。

  • 自然语言查询:用户可以使用 AND、OR、IN 等关键词灵活组合文本查询,减少 SQL 语句编写的复杂性。

更多功能,请参考 Tantivy 文档

与 AI 数据库的无缝集成

我们的 AI 数据库的编写语言是 C++,为了丰富其全文搜索功能,我们需要一个可以直接嵌入到 AI 数据库中的库。

Tantivy 是一个受 Apache Lucene 启发的全文搜索库。与 Elasticsearch、Apache Solr 和其他类似引擎不同,Tantivy 可以集成到各种数据库中。另一方面,Tantivy 使用 Rust 编写,可以使用 Corrosion 轻松地与 C++ 程序集成。

集成过程

为 Tantivy 构建 C++ 包装器

构建 C++ 包装器

我们无法直接在 AI 数据库中使用原始的 Tantivy 库。为了解决跨语言开发(C++ 和 Rust)的挑战,我们开发了tantivy-search,这是一个用于 Tantivy 的 C++ 包装器。它为 AI 数据库提供了一组 FFI 接口,可以直接管理索引的创建、销毁、加载,并能够灵活处理各种场景下的文本搜索需求。

将 Tantivy 实现为 ClickHouse 的跳跃索引

ClickHouse 的跳跃索引主要用于加速带有 WHERE 子句的查询。我们实现了一种名为 FTS(全文搜索)的新跳跃索引类型,以 Tantivy 作为底层实现。因此,对于 CK 中具有 FTS 索引的每个数据部分,我们为其构建一个 Tantivy 索引。如前所述,Tantivy 为每个索引生成了多个段文件。为了减少需要存储在数据部分中的文件数量,我们将这些段文件序列化为两个文件,并将它们存储在数据部分中。skp_idx_[index_name].meta文件记录了每个段文件的名称和偏移量,而skp_idx_[index_name].data文件存储了每个段文件的原始数据。

实现跳跃索引

Tantivy 利用内存映射(mmap)来访问段文件。这种方法不仅提高了并发搜索速度,还增强了索引构建效率。由于Tantivy 无法直接将skp_idx_[index_name].data文件映射到内存中,当用户发起需要 FTS 索引的查询时,AI 数据库将索引文件(.meta.data)反序列化为 Tantivy 段文件,并加载 Tantivy 索引到临时目录中。Tantivy 通过内存映射加载这些反序列化的段文件,以执行各种类型的文本搜索。因此,用户的初始查询请求可能需要几秒钟才能完成。

在 AI 数据库服务中,我们将 Tantivy 的段索引文件存储在 NVMe SSD 上。这减少了 I/O 等待时间,并提高了在需要随机访问和处理页面错误异常的场景中的内存映射性能。

增强 ClickHouse 的本地文本搜索功能

当在包含 FTS 索引的列上发起带有过滤条件的请求时,AI 数据库首先访问 FTS 索引。它检索满足 SQL 过滤条件的列的所有行 ID,并将这些行 ID 存储在一个高级位图数据结构中,称为 Roaring Bitmap。在遍历粒度时,我们确定粒度的行 ID 范围是否与位图相交,从而判断是否可以丢弃该粒度。最终,数据库只访问那些未被丢弃的粒度,从而实现查询加速。

增强 CK 本地搜索

理想情况下,跳跃索引确实可以加速查询,但我们发现其效果有限。如果搜索的词项几乎出现在所有粒度中,AI 数据库只能跳过少量的粒度,需要访问大量的粒度进行查询,使得跳跃索引在这种情况下失效。为了解决这个问题,我们引入了TextSearch函数,不仅解决了跳跃索引的低效问题,还带来了其他实用功能。

引入 TextSearch 函数

为了充分利用 Tantivy 的全文搜索功能,我们将TextSearch函数纳入到 AI 数据库中。这允许用户执行模糊文本检索请求,并获得按 BM25 得分相关性排序的一组文档。此外,用户可以在 TextSearch 函数中使用自然语言查询,大大降低了 SQL 编写的复杂性。

引入 TextSearch 函数

TextSearch函数在搜索文本时从表中检索前 K 个最相关的结果。在执行方面:

  1. AI 数据库同时对所有数据部分执行TextSearch 文本检索。因此,每个部分都收集按 BM25 得分排序的前 K 个最相关的结果。

  2. 数据库根据 BM25 得分从数据部分获取的这些结果进行聚合。

  3. 根据用户 SQL 查询中指定的ORDER BYLIMIT子句,数据库保留前 K 个结果。

TextSearch 函数不直接从数据部分中读取数据,而是通过 Tantivy 直接检索索引搜索结果,因此非常快速。

需要注意的是,AI 数据库使用多个数据部分来存储数据,每个数据部分负责存储整个表数据的一部分。我们不能简单地对每个部分对应的相同答案文本的 BM25 得分进行平均并排序。这是因为每个部分在计算 BM25 得分时,只考虑当前部分内的“总文档数”、“总词项数”和“文档频率”,而不考虑其他部分内与 BM25 算法相关的其他参数。因此,这会导致最终合并结果的准确性降低。

为了解决这个问题,我们在启动 TextSearch 查询之前,首先计算每个部分内的 BM25 统计信息,然后将它们合并为整个表的逻辑对应的 BM25 统计信息。此外,我们修改了 Tantivy 库,以支持共享的 BM25 信息的使用。这确保了在多个部分之间的 TextSearch 搜索结果的正确性。

下面是在 ms_macro 数据集上使用 TextSearch 函数执行基本文本搜索的简单示例。

SELECT
    id,
    text,
    TextSearch(text, 'who is Obama') AS score
FROM ms_macro
ORDER BY score DESC
LIMIT 5

输出:

id text score
2717481 Sasha Obama Biography. Name at birth: Natasha Obama. Sasha Obama is the younger daughter of former U.S. president Barack Obama. Her formal name is Natasha, but she is most often called by her nickname, Sasha. Sasha Obama was born in 2001 to Barack Obama and his wife, Michelle Obama, who were married in 1992. Sasha Obama has one older sister, Malia, who was born in 1998. 15.448088
5016433 Sasha Obama Biography. Sasha Obama is the younger daughter of former U.S. president Barack Obama. Her formal name is Natasha, but she is most often called by her nickname, Sasha. Sasha Obama was born in 2001 to Barack Obama and his wife, Michelle Obama, who were married in 1992. Sasha Obama has one older sister, Malia, who was born in 1998. 15.407547
564474 Michelle Obama net worth: $11.8 Million. Michelle Obama Net Worth: Michelle Obama is an American lawyer, writer and First Lady of the United States who has a net worth of $11.8 million.Michelle Obama was born January 17, 1964 in Chicago, Illinois.ichelle Obama net worth: $11.8 Million. Michelle Obama Net Worth: Michelle Obama is an American lawyer, writer and First Lady of the United States who has a net worth of $11.8 million. 14.88242
5016431 Name at birth: Natasha Obama. Sasha Obama is the younger daughter of former U.S. president Barack Obama. Her formal name is Natasha, but she is most often called by her nickname, Sasha. Sasha Obama was born in 2001 to Barack Obama and his wife, Michelle Obama, who were married in 1992. 14.63069
1939756 Michelle Obama Net Worth: Michelle Obama is an American lawyer, writer and First Lady of the United States who has a net worth of Michelle Obama Net Worth: Michelle Obama is an American lawyer, writer and First Lady of the United States who has a net worth of $40 million. Michelle Obama was born January 17, 1964 in Chicago, Illinois. She is best known for being the wife of the 44th President of the United States, Barack Obama. She attended Princeton University, graduating cum laude in 1985, and went on to earn a law degree from Harvard Law School in 1988. 14.230849

性能评估

我们使用clickhouse-benchmark对 AI 数据库在不同索引下的搜索性能进行了比较,包括其实现的 FTS 索引、ClickHouse 内置的倒排索引以及没有任何索引的情况。

基准测试设置

数据集详情

为了测试 TextSearch 性能,我们使用了 Microsoft 提供的 ms_macro 数据集。ms_macro 数据集包含 8841823 条文本记录,我们将其转换为 parquet 格式,以便轻松导入到 AI 数据库中。此外,我们创建了一组用于测试搜索性能的 SQL 文件,这些文件基于不同的词频。读者可以通过 S3 访问本测试中使用的数据集:

ms_macro_query_files.tar.gz文件包含了本测试中使用的所有 SQL 文件。例如,每个 SQL 文件的名称指示了在数据集中搜索词项的频率以及 SQL 文件中包含的查询数量。例如,ms_macro_count_hastoken_100_100k.sql文件包含 10 万个查询,每个查询中的词在数据集中出现 100 次。

以下是hasTokenTextSearch查询的示例:

SELECT count(*) FROM ms_macro WHERE hasToken(text, 'Crimp');
SELECT count(*) FROM (
    SELECT TextSearch(text, 'Crimp') AS score
    FROM ms_macro ORDER BY score DESC LIMIT 10000000
) as subquery;
测试环境

尽管我们的测试环境具有 64GB 的内存,但在测试过程中,AI 数据库的内存消耗保持在 2.5GB 左右。

数据导入过程

为 ms_macro 数据集创建表:

CREATE TABLE default.ms_macro
(
    `id` UInt64,
    `text` String
)
ENGINE = MergeTree
ORDER BY id
SETTINGS index_granularity = 128;

将数据直接从S3导入到 AI 数据库:

INSERT INTO default.ms_macro
SELECT * FROM
s3('https://myscale-datasets.s3.ap-southeast-1.amazonaws.com/ms_macro_text.parquet','Parquet');

合并 ms_macro 的数据部分以提高搜索速度。请注意,此操作是可选的。

OPTIMIZE TABLE default.ms_macro final;
SELECT count(*) FROM system.parts WHERE table = 'ms_macro';

输出:

验证 ms_macro 包含 8841823 条记录:

SELECT count(*) FROM default.ms_macro;

输出:

索引创建

我们将评估三种类型的索引的性能:FTS、倒排和无索引(没有任何索引的情况)。

  • 创建 FTS 索引
-- 确保在创建FTS索引时,ms_macro表的text列上不存在其他索引。
ALTER TABLE default.ms_macro DROP INDEX IF EXISTS fts_idx;
ALTER TABLE default.ms_macro ADD INDEX fts_idx text TYPE fts;
ALTER TABLE default.ms_macro MATERIALIZE INDEX fts_idx;
  • 创建倒排索引
-- 确保在创建倒排索引时,ms_macro表的text列上不存在其他索引。
ALTER TABLE default.ms_macro DROP INDEX IF EXISTS inverted_idx;
ALTER TABLE default.ms_macro ADD INDEX inverted_idx text TYPE inverted;
ALTER TABLE default.ms_macro MATERIALIZE INDEX inverted_idx;
  • 无索引:确保 ms_macro 表的 text 列不包含任何索引。
运行基准测试

使用 clickhouse-benchmark 进行压力测试。有关更多使用说明,请参考ClickHouse文档

clickhouse-benchmark -c 8 --timelimit=60 --randomize --log_queries=0 --delay=0  < ms_macro_count_hastoken_100_100k.sql -h 127.0.0.1 --port 9000

评估结果

评估结果

从比较结果可以看出,当搜索词的频率较高(100K1M)时,跳跃索引的加速效果非常有限(与没有建立任何索引的性能相比,仅提高了十倍)。然而,当搜索词的频率较低(1001K)时,跳跃索引可以实现显著的加速效果(与没有建立任何索引的性能相比,提高了多达一百倍)。

另一方面,TextSearch函数在所有场景下始终优于跳跃索引和倒排索引。这是因为TextSearch直接利用了Tantivy的全文搜索功能,无需扫描粒度,而是直接从索引中检索结果。这导致搜索过程更快、更高效。

结论

将 Tantivy 集成到我们的 AI 数据库中显著增强了其文本搜索功能,使其成为文本数据分析和基于大型语言模型(LLM)的检索增强生成(RAG)的强大工具。通过解决 ClickHouse 本地文本搜索功能的局限性,并引入 BM25 相关性评分、可配置的分词器和自然语言查询等高级功能,AI 数据库现在为复杂的文本搜索需求提供了强大而高效的解决方案。

为 Tantivy 构建 C++ 包装器、创建新的跳跃索引以及引入TextSearch函数都对此改进做出了贡献。这些增强不仅提升了 AI 数据库的性能,还扩展了其用例,使其成为各种应用中高效准确的文本搜索的首选。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐