ClickHouse 内部的全文搜索:快速、原生、列式架构

clickhouse到底有哪些吊炸天的优化? 从技术实现上来说,肯定是可行的。上面有三个part,现在我的诉求是查询出其中>=3且<=5的所有数据,虽然在各个part内部,数据本身是有序的,但是这三个part很不凑巧地都包含了3~5的范围,因此必须每个part都扫描一遍,而实际上,part3是没有任何符合条件的数据的,所以这个扫描其实是无用的操作。热数据,经常会被查询的数据(比如近两三天的数据),存储到固态磁盘,这样不论是写入,还是查询都非常快,超过一定时间的数据,可以通过存储策略转移到机械磁盘上,因为查询频率并不怎么高,那么受到的影响就会有限。 阅读详情

图片

本文字数:13269;估计阅读时间:34 分钟

作者:Jimmy Aguilar, Elmi Ahmadov, and Robert Schulze

本文在公众号【ClickHouseInc】首发

图片

简而言之(TL;DR)

我们在 ClickHouse 中彻底重构了全文搜索功能,性能更高、更轻量,并且与列式数据库架构深度融合。  

这篇文章由参与构建该功能的工程师撰写,将深入解析新版架构,从倒排索引到查询优化技巧——包括如何在查询中完全跳过读取文本列。如果你希望在数据库内部实现高性能搜索,或者对数据库底层机制充满兴趣,这篇文章值得一读。

从旧架构到极速体验:ClickHouse 全文搜索的演进之路

虽然全文搜索(Full-text search,简称 FTS)早已在 ClickHouse 中出现,但此前的版本存在性能和灵活性方面的限制。本次我们从零开始,全面重构全文搜索功能,使其运行更快、更节省空间,并且深度融入 ClickHouse 的列式存储架构。

点击这个链接(https://clickhouse.com/cloud/inverted-index-waitlist)即可申请新版全文搜索功能的私测资格。

这将是一场深入底层机制的探索,我们将介绍核心数据结构——倒排索引、有穷状态自动机(finite state transducers)、倒排列表(posting lists)——以及查询执行流程的重新设计方式,如何减少 I/O 操作并提升搜索速度。

如果你曾经遇到以下场景:

  • 想在 ClickHouse 内直接运行搜索类分析,

  • 希望省去维护独立搜索引擎的成本,

  • 想深入理解现代列式存储中的文本索引机制,

……这篇文章将为你带来完整的原理解析与实践方法,并结合真实示例展示可量化的性能收益。

我们从新版索引如何存储与组织文本说起,这是一切优化的基础。

全文搜索为何如此高效?

设想你在 ClickHouse 中存储产品评论、日志信息或用户留言。每行数据代表一段自然语言文本——即由人或机器撰写、符合自然语言语法的内容(而非随机 ID 或哈希值)。

由于 ClickHouse 是一款列式数据库,这些文档被依次存储在一个 String(https://clickhouse.com/docs/en/sql-reference/data-types/string/) 类型的列中,每行对应一个可供搜索的文档。

为了支持搜索,ClickHouse 会将每个文档拆分成一系列的 Token(词元),通常对应单词。例如,句子:

All cats like mice

将被分词为:

[All, cats, like, mice]

默认的分词器会按空格或标点将字符串切分成若干词元。更高级的分词器还能将日志消息拆解为多个字段,如时间戳、严重性等级和具体消息,甚至支持提取 n-gram,以实现模糊搜索。

全文搜索(Full-text search) 的基本原理是查找包含某个特定词元的文档。比如,为了查找所有提及 cat 的行,我们可能会执行如下查询:

SELECT [...] FROM [...] WHERE documents LIKE '% cat %'

(注意空格的重要性 —— 如果忽略空格,可能会误匹配如 “edu cat e” 或 “multipli cat ion” 这样的词。)

全文搜索主要有两种实现方式:

  • 全表扫描:遍历所有行(速度较慢)

  • 倒排索引:将每个词元映射到其所在的行(速度较快)

倒排索引可以让我们在毫秒级完成对海量数据的搜索,这正是 ClickHouse 现在原生支持的特性,具备良好的扩展性与性能表现。

在深入介绍其实现细节之前,我们先来看看“倒排索引(Inverted Index)”这个术语的含义。它是一种简单却可能让人感到直观上难以理解的结构:它颠覆了我们通常对文档和词项关系的认知——从“文档 → 词项”变成了“词项 → 文档”。

为什么称它为倒排索引?

当你按顺序阅读多个文档时,通常会记录每个文档中包含的唯一词项。这文档 → 词项的映射结构被称为正排索引(forward index)。

而 倒排索引(inverted index) 则反其道而行之:它保存的词项 → 文档的映射关系。也就是说,给定一个词项,你可以迅速查找到所有包含该词项的文档。

这类似于书籍的索引页:你无需翻阅整本书去寻找某个词,而是可以直接查找该词在哪些页码上出现。正是这种反向查找方式,使得搜索引擎可以实现快速检索,而 ClickHouse 已将这一机制原生集成。

既然我们已经了解了什么是倒排索引,那么接下来我们不妨看看 ClickHouse 的起点。虽然 ClickHouse 早期就提供了文本查找加速方式,但本质上仍属于概率型近似方案。为了说明为何需要真正的倒排索引,我们将回顾从最初基于 Bloom 过滤器的方案到现在新架构的演进过程。

从 Bloom 过滤器到真正的索引

在引入原生文本索引功能之前,ClickHouse 已支持使用 "bloom_filter"、"tokenbf_v1" 和 "ngrambf_v1" 索引实现全文搜索。这些机制基于 Bloom 过滤器——一种用于判断某个值是否存在于已索引文档中的概率型数据结构。

与倒排索引相比,Bloom 过滤器存在几个显著限制:

  • 难以调优: 使用 Bloom 过滤器时需要手动设置字节大小、哈希函数数量,并了解这些参数如何影响误报率。这通常需要较高的专业技术门槛。

  • 存在误报: Bloom 过滤器可能会返回“值可能存在”的结果,即使该值实际上并不在文档中。这导致系统仍需对更多行执行扫描操作,降低了整体搜索效率。

  • 功能受限: Bloom 过滤器索引仅支持 WHERE 子句中的部分表达式,而倒排索引从原理上具有更强的通用性与表达能力。

倒排索引有效解决了上述三大问题,因此成为 ClickHouse 当前全文搜索功能的核心技术基础。

新文本索引的工作原理

要理解新引擎如何实现高性能与精准查询,首先需要了解索引在磁盘中的组织方式,以及查询过程是如何与其内部结构协同工作的。

索引结构:字典 + 倒排列表

ClickHouse 的全文搜索基于一种原生倒排索引,也称为 文本索引(text index)。从结构上看,该索引由两个核心组成部分构成:

1. 字典(dictionary):用于存储所有文档中出现的唯一 Token(词元)。

2. 倒排列表(posting lists):记录每个词元所在文档的行号信息。

其运行机制如下:

  • 当你查询某个词元时,ClickHouse 会首先在字典中查找该词元。

  • 如果找到,字典会返回该词元对应的倒排列表位置。

  • 倒排列表本质上是一个行号数组,即包含该词元的所有文档所在的行。

  举例来说:

  • 词元 wind 可能出现在文档 12、15、99、100 和 141 中。

  • 词元 winter 可能出现在文档 12、514、678 和 2583 中。

这种索引结构,即使在面对海量数据时,也能实现高效的词元查找。

下面是这一结构的简化图示:

图片

在底层,ClickHouse 通过高级数据结构对字典和倒排列表进行压缩存储,下一节将深入解析其内部细节。

FST:高效节省空间的字典结构

为了快速查找词元,我们需要一种既高效又节省空间的字典结构。

最基础的实现方式是使用一个排好序的 (词元 → 倒排列表) 映射列表。这样我们可以通过快速的二分查找定位目标词元。考虑到文档通常为自然语言文本,这也带来了一个优化空间:前缀共享(prefix sharing)。

例如,许多词元具有相同的前缀:

  • " win d"," win ter"

  • " click "," click house"

普通的词元列表无法利用这些前缀相同的特征,但 有限状态转导器(Finite State Transducer,FST) 却可以很好地支持这种压缩方式。

什么是 FST?

FST(有限状态转导器)是一种紧凑的自动机结构,本质上是由字符构成的图,用于以高度压缩的方式编码词典。它最初设计用于实现字符串之间的转换,但也非常适合用于文本索引。像 Lucene(https://burntsushi.net/transducers/) 和 ClickHouse 这样的系统,就利用 FST 来表示排序后的字典。

与将每个 Token 直接存储为字符串不同,FST 具有以下优势:

  • 对共享的前缀和后缀仅存储一次;

  • 每个 Token 都被编码为图中的一条路径;

  • 当路径到达最终的“接受状态”结点时,FST 会输出一个值,例如该词元对应倒排列表的地址。

这种结构尤其适合多个 Token 拥有共同前缀或后缀的场景,使得整个字典更加紧凑。

Token 到倒排列表的映射

需要注意的是,FST 并不直接存储倒排列表内容,而是提供了定位它们的方法。具体来说,每个 Token 都对应一个倒排列表偏移量,即该列表在另一独立文件中的起始字节位置。

为了获取某个 Token 的偏移量,我们从 FST 的起始状态出发,逐步遍历路径直到接受状态。过程中,每次状态跳转可能会输出一个整数,最终通过累加这些数值得到对应的偏移量。虽然听起来略显复杂,但这正是标准 FST 构造算法的运作方式(参考文献:ref(https://link.springer.com/chapter/10.1007/3-540-44674-5_18))。

示例

假设我们有三个 Token:

ClickBench、ClickHouse、JSONBench

我们可以基于这组 Token 构建一个对应的 FST,并观察其图结构。

图片

在 FST 中,这些 Token 被表示为字符之间的状态转换路径。例如,"Click" 作为公共前缀会被多条路径复用。当每个词元的路径到达终点时,FST 会输出相应的偏移值(如 10、20、30):

图片

因此,FST 实现了两个关键功能:

  • 快速判断某个词元是否存在;

  • 精确计算该词元对应倒排列表的字节偏移量。

通过这种机制,ClickHouse 能够以高度压缩、快速可查的方式存储大规模词元字典。

Roaring Bitmaps:高效的集合操作

每个 倒排列表(posting list) 存储的是包含某个特定词元的所有文档行号。

为了在执行搜索时兼顾性能和存储效率,ClickHouse 需要一种同时具备以下特性的存储结构:

  • 紧凑:尽可能节省存储空间;

  • 高效:能够快速执行集合间的交、并、差等操作。

这种集合运算能力在处理包含多个词元并通过逻辑运算符组合的查询时尤为重要,例如执行多个词的“与”查询(AND)、“或”查询(OR)或排除查询(NOT)。

LIKE '% cat %' AND LIKE '% dog %'

为什么压缩至关重要

传统的倒排列表压缩方案通常结合以下两种技术:

  • 增量编码(Delta encoding):通过记录相邻数值之间的差值来减少冗余;

  • Golomb-Rice 编码:对这些差值进行高效压缩。

这类方案在处理自然语言文本时通常效果不错,因为自然语言往往包含少量高频词和大量低频词。然而,它们以“位”为单位处理数据,不利于现代 CPU 的流水线机制和 SIMD 指令优化,因此在性能上存在瓶颈。

引入 Roaring Bitmaps

为了提升处理效率,ClickHouse 采用了 Roaring Bitmap(https://roaringbitmap.org/),这是一种专为大规模整数集合设计的高性能存储格式。

其核心设计理念如下:

  • 将倒排列表视为一个 位图(bitmap),例如 [3, 5, 8] 可表示为 000101001;

  • 将整个位图划分为多个 块(chunk),每块包含 65,536(2¹⁶)个可能值;

  • 每个块根据其稀疏或密集程度,采用不同的容器进行存储:

    • 数组容器(Array):用于稀疏数据;

    • 位图容器(Bitmap):用于密集数据;

    • 游程编码容器(Run-length encoding):适用于长连续数据段。

这种结构不仅能有效压缩存储空间,还支持极快速的集合运算,如多个倒排列表之间的 AND、OR、NOT 操作。为了进一步提速,Roaring Bitmaps 还配有专门的 SIMD 加速实现,充分发挥现代处理器的性能优势。

可视化解析

在 Roaring Bitmap 中,每个 32 位的行号会被拆分为两个 16 位部分:

  • 高 16 位:用于确定所属容器;

  • 低 16 位:作为该容器内实际存储的值。

举个例子:

  • 行号:124586

  • 二进制表示为:00000000 00000001 11100110 11101010

  • 高 16 位:00000000 00000001 → 表示选择的容器

  • 低 16 位:11100110 11101010 → 存储于容器中的值

图片

每个容器只负责某个特定的值区间,并根据其数据密度采用最合适的压缩格式。这种结构设计,使得系统能够高效地完成数十亿行号的扫描、合并和过滤操作。

到目前为止,我们已经介绍了 ClickHouse 文本索引的两个核心组成:

  • FST:构建紧凑、支持前缀共享的 Token 字典;

  • Roaring Bitmap:实现快速、压缩高效的倒排列表存储。

通过这两种结构,字典与倒排列表得以在存储层面实现高效组织。接下来我们来看整个索引在磁盘上的具体组织方式。

存储布局:段(segments)、粒度(granules)与文件结构

现在我们将所有组件组合起来,完整展现倒排索引的磁盘布局。

每个倒排索引在磁盘上由五个文件构成:

1. ID 文件:记录段 ID 及其版本信息(关于“段”的定义见下文);

2. 元数据文件(metadata):维护所有段的元信息;

3. Bloom Filter 文件:存储布隆过滤器,用于在可能的情况下避免加载字典和倒排列表;

4. 字典文件:以 FST 格式存储各个段的 Token 字典;

5. 倒排列表文件:存储所有倒排列表,以压缩整数序列的形式表示。

可参考下图,了解这些文件在磁盘中的整体结构。

数据是如何组织的

每个数据 part 都会构建对应的索引,并在 合并操作(merges)(https://clickhouse.com/docs/merges) 中进行重建。在内部,索引被划分为若干个 段(segments),每个段包含以下内容:

  • 一个基于 FST 的词元字典;

  • 多个倒排列表(使用 Roaring Bitmap 表示),每个词元对应一个列表。

元数据文件用于记录以下信息:

  • 段 ID;

  • 起始行号(row ID);

  • 在 Bloom Filter 文件、字典文件、倒排列表文件中的偏移位置。

图片

在上图中,箭头①、②、③展示了在查询过程中各个文件的加载顺序。

不同类型的文件,其大小也各不相同:

  • Bloom Filter 文件体积最小;

  • 字典文件体积适中;

  • 倒排列表文件体积最大。

段(Segment)与索引颗粒(Index Granule)

要理解“段”的概念,需先介绍 ClickHouse 中的 索引颗粒(granules),详见官方文档:链接(https://clickhouse.com/docs/guides/best-practices/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing)。

  • 索引颗粒 是 ClickHouse 的基本索引单位,通常包含 8192 行数据;

  • 段(segment) 是颗粒中的进一步划分单元,设计初衷是降低索引构建时的内存占用。

段的划分依据是字典的大小,而非行数。这种策略可以确保每个段的 FST 字典规模大致一致,从而在处理大规模数据集时,避免出现内存溢出的问题。

每个段都是独立的存储单元,包含:

  • 一段行号范围;

  • 一个 FST 格式的词元字典;

  • 多个对应的倒排列表。

虽然较小的段有助于减少内存压力,但也可能由于多个段中重复存储相同的词元而导致额外的存储开销。段划分功能默认未开启,但对于某些高级用例可选择启用。

索引查询的执行流程

当执行搜索查询时,ClickHouse 是通过以下步骤定位相关的文档行:

1. 遍历所有段中的字典:

系统会在每个段的 FST 中查找目标词元。当遍历路径到达接受状态,说明该词元在对应段中存在。

2. 累加 FST 输出值:

这些值代表该词元在倒排列表文件中的偏移位置,用于后续加载操作。

3. 加载对应的倒排列表:

  • 在“冷路径”(cold run)中,倒排列表需要从磁盘加载;

  • 在“热路径”(hot run)中,倒排列表可能已被缓存在内存中。

注意:倒排列表中记录的行号是相对于段起始行号的“相对值”,在查询阶段会被转换为完整的颗粒级行号。(后文将在“直接索引使用优化”部分中进一步说明。)

在掌握了基本结构后,我们来看看新版中对文本索引的关键性优化,包括 API 设计改进、内存使用优化,以及更智能的颗粒处理等方面。

近期功能优化一览

过去三个月,ClickHouse 在全文搜索能力上取得了显著进展,涵盖从用户界面到底层存储的全面升级。以下是当前已支持以及即将发布的新功能:

1. 更简洁的 API 设计

我们将索引名称从 “inverted” 改为更具语义的 “text”,同时重新设计了语法结构,使其更加直观易用。

现在支持在 INDEX 子句中以键值对形式配置参数,这不仅提升了可读性,也增强了配置的灵活性。

CREATE TABLE tab
(
    [...],
    documents String,
    INDEX document_index(documents) TYPE text(tokenizer = 'default')
)
ENGINE = MergeTree
ORDER BY [...];

如下示例将创建一个文本索引,使用默认分词器(基于非字母数字字符进行分词)。你也可以选择其他分词器,如 'ngram'、'split' 或 'no_op',并进一步指定参数,例如设置 n-gram 的大小。

2. 新增 split 分词器

我们引入了一种全新的分词器类型:split,专为日志、CSV 等半结构化文本场景设计。

与默认分词器根据通用语言规则自动识别单词不同,split 分词器仅在指定的分隔符处进行拆分(默认分隔符包括:,、;、\n 和 \\)。

这种方式特别适合以下使用场景:

  • 类似 CSV 格式的结构化输入;

  • 使用固定分隔符的日志内容;

  • 任何需要精确控制词元边界的文本处理任务。

示例:CSV 格式文本

以下是默认分词器与 split 分词器在处理某个 CSV 样式字符串时的差异表现:

SET allow_experimental_full_text_index = 1;

CREATE TABLE tab_csv_default (
  id Int64,
  str String,
  INDEX idx_str(str) TYPE text(tokenizer = 'default')
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE tab_csv_split (
  id Int64,
  str String,
  INDEX idx_str(str) TYPE text(tokenizer = 'split', separators = [',', ';', '$'])
)
ENGINE = MergeTree()
ORDER BY id;

插入一个多行 CSV 风格的示例值

INSERT INTO tab_csv_default (id, str) VALUES
(1,
'Click
House,ClickHouse,JSONBench'
);

INSERT INTO tab_csv_split SELECT * FROM tab_csv_default;

split 分词器的处理效果

默认分词器会将上述内容按常规方式拆分为多个词元,

['Click', 'House', 'ClickHouse', 'JSONBench']

它忽略了 “Click” 与 “House” 之间的换行符,因此不会将 "Click\\nHouse" 视为一个完整的 Token。

而 split 分词器则严格按照定义的分隔符进行拆分。由于换行符 “\\n” 并不匹配默认分隔符的精确边界位置,因此会保留 "Click\\nHouse" 作为一个完整词元,不会被进一步拆分。

搜索行为说明

在搜索过程中,不同分词器对多行文本的处理存在差异:

默认分词器:无法匹配跨行的词元;

SELECT * FROM tab_csv_default WHERE searchAny(str, ['Click\nHouse'])

Ok.

0 rows in set. Elapsed: 0.001 sec.

split 分词器:能够正确识别并命中完整的多行 Token。

SELECT * FROM tab_split WHERE searchAny(str, ['Click\nHouse'])


┌─id─┬─str────────────────────────┐
│  1 │ Click                     ↴│
│    │↳House,ClickBench,JSONBench │
└────┴────────────────────────────┘

1 row in set. Elapsed: 0.003 sec.

核心结论:

当你需要基于精确定义的分隔符进行分词,且希望保留跨行或特殊格式的词元时,建议使用 split 分词器。

3. 更小的索引体积

我们对文本索引在内存和磁盘层面的占用进行了显著优化:

  • 倒排列表采用 PFOR 压缩  鉴于交集与并集操作仅在内存中的倒排列表上执行,对于磁盘存储部分,我们可以优先考虑压缩率。因此,在原有 Delta 编码基础上,我们引入了 PFOR(Patched Frame of Reference)压缩算法。该改进使倒排列表的磁盘占用最多可减少约 30%。

  • FST 引入 Zstd 压缩  FST 在写入磁盘时现已默认启用 Zstd 压缩,平均体积进一步降低约 10%。

4. 完善的云端兼容性

文本索引现已全面兼容 ClickHouse Cloud,包括对云环境中使用的 packed part 数据格式的支持。

5. 更智能的索引粒度设置

我们将默认索引粒度从 1 提升至 64。

粒度指的是每个索引颗粒覆盖的行数范围,详见:索引粒度说明 →(https://clickhouse.com/docs/optimize/skipping-indexes)

  • 较小粒度:可以实现更精细的过滤,但由于词元在多个段中重复出现,会带来更高的内存消耗和磁盘 I/O。

  • 较大粒度:能显著降低资源开销,对大多数常见词元性能更优,但在处理极少数或聚集性强的词元时,过滤精度可能略有下降。

通过大量实验,我们发现将粒度设为 64 能在多数使用场景下带来优异表现,因此该设置已成为默认值。

在上述核心优化之外,我们还新增了若干功能,以进一步提升查询效率并减少资源消耗。其中之一就是引入布隆过滤器作为预过滤层,用于在加载倒排列表和字典前快速判断是否需要查询。

6. 作为预过滤机制的 Bloom Filter

Bloom Filter 是一种轻量级的概率型数据结构,用于高效判断某个元素是否存在于集合中。它的判断结果具有以下特点:

  • 一定不在集合中时可明确排除;

  • 可能在集合中,但存在一定概率的误报(false positive),误报率可调。

由于 Bloom Filter 永远不会出现假阴性(false negative),它非常适合作为前置判断工具,在执行较重的计算或 I/O 操作前,先快速排除不可能匹配的项。

在 ClickHouse 中,我们将 Bloom Filter 引入为文本索引的预过滤层。这意味着,在扫描 FST 字典之前,可以先通过 Bloom Filter 判断词元是否*确定不存在*,从而有效降低磁盘读取和 CPU 开销。由于 Bloom Filter 占用空间小,通常可以常驻内存中,提升访问效率。

文本索引 vs Bloom Filter 索引:两者区别

在全文搜索中,Bloom Filter 的使用方式主要有两种:

1. 作为独立的 Bloom Filter 文本索引:将词元直接插入 Bloom Filter 中;

2. 作为附加层叠加在文本索引段上:由段内字典自动生成 Bloom Filter。

如前所述,基于 Bloom Filter 的独立索引存在一个主要缺点:需要用户手动设置参数(如大小、误报率等),调优复杂。

而第二种方式则是完全自动的,基于实际索引数据和可配置的误报率(默认值为 0.1%)生成 Bloom Filter,无需用户干预,使用更简单,适用性更强。

通常建议:除非对索引精度有特殊要求,否则推荐使用文本索引而非 Bloom Filter 索引。

除了在结构层引入预过滤机制,我们还进一步优化了查询接口,引入了更现代、灵活的搜索函数。

7. 新增搜索函数:searchAny 与 searchAll

现代问题,需要现代方法。

随着 ClickHouse 对分词器的支持不断扩展,并引入了多种自定义参数,原有的 “hasToken” 函数逐渐显得局限:它总是使用默认分词器来处理查询词,而不考虑索引实际使用的分词方式。这种行为常导致结果不符合预期,因此我们对此进行了改进。

我们新增了两个用于全文搜索的函数:searchAny 和 searchAll,以支持更加灵活、准确的查询。

简单示例

假设我们有如下建表语句及数据:

SET allow_experimental_full_text_index = 1;

CREATE TABLE tab (
  id Int64,
  str String,
  INDEX idx_str(str) TYPE text(tokenizer = 'ngram', ngram_size = 5)
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO tab (id, str) VALUES
  (1, 'Click House'),
  (2, 'ClickHouse');

使用旧函数 hasToken 查询如下:

SELECT * FROM tab WHERE hasToken(str, 'Click');
┌─id─┬─str─────────┐
│  1 │ Click House │
└────┴─────────────┘

该查询不会返回结果。

原因如下:

hasToken 函数要求词元前后必须存在非字母数字字符作为分隔符。由于 “ClickHouse” 是一个完整单词,未被分隔,因此匹配失败。

如果改用 searchAny 函数:

SELECT * FROM tab WHERE searchAny(str, ['Click']);

则返回两行匹配结果:

┌─id─┬─str─────────┐
│  1 │ Click House │
│  2 │ ClickHouse  │
└────┴─────────────┘

在该示例中,使用 searchAll 同样会返回相同结果。

新函数优势

无需特殊分隔符

新引入的函数不再依赖词元周围的特殊字符,它们会直接使用索引中配置的分词器进行解析。

原生支持多个词元查询

例如,原本需要写作如下形式:

SELECT * FROM tab WHERE hasToken(str, 'Click') AND hasToken(str, 'House');

SELECT * FROM tab WHERE hasToken(str, 'Click') OR hasToken(str, 'House');

现在可以更简洁地写作:

SELECT * FROM tab WHERE searchAll(str, ['Click', 'House']);

SELECT * FROM tab WHERE searchAny(str, ['Click', 'House']);

语法更简洁,行为更一致

这些新函数始终依据索引中定义的分词器与参数配置执行,避免因分词器不一致而导致的查询偏差。

注意:searchAny 与 searchAll 仅适用于已创建全文索引的列。详细说明可参考官方文档:searchAny(https://clickhouse.com/docs/en/sql-reference/functions/string-search-functions#searchany) 和 searchAll(https://clickhouse.com/docs/en/sql-reference/functions/string-search-functions#searchall)。

虽然这些函数带来了更高的可用性与灵活性,但真正实现性能飞跃的,是一项关键优化 —— 跳过文本列读取的能力。

实现无需读取文本列的高性能行级搜索

ClickHouse 中的倒排索引本质是一种跳过式索引(skip index),用于在查询的第一阶段快速排除不相关的数据颗粒(granules)。仅将可能包含目标词元的颗粒保留下来,并在第二阶段从磁盘加载这些颗粒并逐行进行匹配过滤。

这种机制对于低频词元效果非常显著,能够大幅降低 I/O 与处理开销。但若查询的是高频词元,极有可能导致多数甚至全部颗粒通过第一阶段筛选,进而在第二阶段形成接近全表扫描的实际执行成本。

全文搜索的新突破

我们彻底解决了全文搜索中的关键性能瓶颈。

倒排索引现在支持直接在行级别进行过滤,完全基于索引完成匹配,无需读取文本列。

该优化在现有颗粒级过滤逻辑基础上,引入了一个全新的行级匹配引擎,对用户完全透明,无需更改任何查询语句。

示例场景

考虑如下查询:

SELECT col1, col2 FROM table WHERE searchAny(col3, ['hello'])

优化前:必须读取文本列才能识别匹配行

  • 查询引擎必须从已建立索引的 col3 文本列中读取内容,以逐行判断哪些行符合查询条件;

  • 即使前期已使用索引排除了部分不相关颗粒,仍需扫描大量行内容;

  • 由于文本列通常较大,且缺乏行级索引支持,这种方式处理效率偏低。

优化后:只依赖索引完成匹配 —— 查询提速高达 10 倍

  • 查询过程中仅读取倒排索引文件;

  • 引擎直接使用索引提取出匹配的行号(row ID),无需访问 col3;

  • 系统仅读取所需返回的列(如 col1 和 col2);

  • 对于高频词元,查询时间可缩短 90% 以上。

下图清晰对比了优化前后执行路径的差异:

优化前的执行流程:

图片

在执行 `hasToken(col3, 'hello')` 时,ClickHouse 必须:

  • 从 col3 文本列中加载颗粒内容,查找匹配行;

  • 读取 col1 和 col2 等其它列用于返回查询结果。

尽管倒排索引已用于跳过部分无关颗粒,但系统仍需对“幸存颗粒”逐行读取并解析文本,以完成最终匹配判断。换句话说,索引只参与了第一阶段的粗筛,未能参与第二阶段的精确匹配。

这种模式在处理大型文本字段时尤为低效,原因包括:

  • 文本列通常占用磁盘较多,读取开销大;

  • 实际需要的信息已保存在体积更小、处理更快的倒排索引结构中。

优化后的执行流程:

图片

新引擎实现完全基于索引的匹配逻辑:

  • 不再读取文本列;

  • 仅加载倒排索引文件;

  • 基于行号直接完成过滤;

  • 仅读取结果所需列。

这一优化彻底打通了全文搜索在 ClickHouse 中的性能瓶颈。

真实业务中的性能收益

我们基于 Hacker News 数据集(https://github.com/ClickHouse/ClickHouse/issues/29693)(约 2700 万行)对全文索引的优化效果进行了测试。

测试覆盖了从高频到低频词元的多种情况:

词元频率(相对出现率)

优化前执行耗时(秒)

优化后冷路径(秒)

优化后热路径(秒)

 90%

1.6 - 2.2

0.43 - 0.6

0.026 - 0.06

50% 

1.4 - 2.0

0.18 - 0.3

0.03 - 0.05

 ~1%

1.4 - 1.8

0.11 - 0.2

0.015 - 0.026

~0%(约 10 条匹配)

0.2 - 0.5

0.11 - 0.15

0.008 - 0.012

(测试运行环境:m6i.8xlarge 实例,32 核 vCPU,128 GiB 内存,Xeon 8375C @ 3.5GHz)

用户无需干预,效果自动生效

这项优化对用户完全透明,无需更改查询语句即可享受性能提升。目前我们也在继续优化冷路径表现,并计划很快将此功能正式开放。

综合这些改进,ClickHouse 的全文搜索已具备更高的执行效率,能够自然地集成进现有分析查询流程中。

至此,搜索体验全面升级

从分词逻辑到全索引路径访问,全新的原生文本索引让 ClickHouse 的搜索能力实现“快、准、深度融合”。无论是模糊匹配、多样化分词器支持,还是无需读取文本列的行级过滤逻辑,都让你可以像写分析 SQL 一样流畅地执行复杂搜索操作。

这一成果源自我们多月的系统重构、性能调优与架构探索,现已准备就绪,欢迎在真实场景中进行体验与验证。

即日起,你可申请参与新版全文搜索功能的私测计划。我们正在征集早期用户的真实反馈,共同推动下一步改进。

立即申请抢先体验(https://clickhouse.com/cloud/inverted-index-waitlist)。

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

GreptimeDB vs. ClickHouse vs. ElasticSearch 日志引擎性能对比报告 GreptimeDB 近期引入了日志存储和检索功能,用户可以使用同样数据模型和查询语言来统一处理指标和日志。本报告测试了 v0.9 首次引入的日志存储和检索的单机性能,并与业内主流方案进行对比。 阅读详情

相关推荐

ClickHouse教程 — 第二章 ClickHouse快速入门

大数据技术之 ClickHouseClickHouse官方介绍ClickHouse介绍篇一|ClickHouse快速入门推荐阅读参考1、2,优先阅读参考1。ClickHouse是俄罗斯的Yandex于2016年开源的用于联机分析(OLAP :Online Analytical Processing的列式数据库管理系统(DBMS:Database Management System),能够使用SQL查询实时生成分析数据报告,主要用于Web流量分析。ClickHouse的全称是。ClickHouse。

2401_89224684的博客 2250

为什么要使用Clickhouse

一、介绍 ClickHouse是一个面向列存储的OLAP分析数据库,以其强大的分析速度而闻名。 二、clickhouse、tidb与mysql: clickhouse是当前查询速度比较快的数据库,非常适合用来分析数据(OLAP),适合数据量特别大的场景,但目前只支持insert,不支持update和delete; tidb和mysql是行数据库,适合用来做数据的增删改操作(OLTP),tidb目前是mysql的替代品,基本兼容mysql协议,在数据量特别大的场景tidb比mysql效率高很多,但在查询方面t

tttalk的博客 3449

ClickHouse与Elasticsearch对比:大数据搜索场景选择

在数字化转型背景下,企业面临PB级数据的实时查询、分析和搜索需求。ClickHouse和Elasticsearch作为两款主流的分布式数据处理引擎,分别在OLAP(在线分析处理)和全文检索领域表现突出,但在技术架构和适用场景上存在显著差异。本文通过技术原理剖析、性能对比和实战案例,解决以下核心问题:核心原理: 数据按列分组存储,每列数据压缩后存储为独立文件(.bin),元数据存储在.mrk文件中。列式存储带来三大优势:数据模型: 支持表引擎包括MergeTree(默认,支持数据排序和版本控制)、Log(简单

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 636

性能优化--全文搜索引擎(Elasticsearch)

一、业务场景 场景1:当数据库表的数据达到一定量的时候,增加索引也不能带来很高的性能优化,但是很多时候又需要各种条件组合查询。 场景2:数据库表进行分表之后,想要将分散到各表的数据聚合在一起,对用户提供查询和搜索服务。 二、解决办法 需要解决大数据量的搜索问题,可以引进全文搜索引擎技术 三、全文搜索引擎技术 四、Elasticsearch ...

guo2312668的博客 2324

Rstan下载安装攻略

准备把Rtools删掉,重新下载(胡乱猜测是安装顺序的问题),但是卸载时显示Rtools被占用,但我已经把该关的都关了。这一问题的出现,直接让我卸载掉了Rstudio,又重新安装了一个,不过这次安装在另一个磁盘,因为原来的内存不多了。或者参考知乎,使用清华镜像网址进行下载:(知乎链接)https://zhuanlan.zhihu.com/p/346947595(清华下载链接)https://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/windows/Rtools/

m0_64643017的博客 3099

完整IC设计流程解析&Makefile学习

本文介绍了decorder38项目的文件结构、自动化综合流程和Makefile使用方法。项目包含RTL到GDSII全流程文件,重点关注syn目录中的综合脚本。通过make dc命令运行综合流程,生成网表、约束和时序文件。关键报告如qor.rpt、area.rpt和min_vio.rpt用于分析综合质量、面积和时序。Makefile部分解释了其基本语法、IC设计中的典型结构,包括目标定义、命令执行和特殊声明,强调了其在自动化构建和依赖管理中的重要性。

m0_63352623的博客 855

【最佳实践】携程ClickHouse日志分析实践

ElasticSearch是一种基于Lucene的分布式全文搜索引擎,携程用ES处理日志,目前服务器规模500+,日均日志接入量大约200TB。随着日志量不断增加,一些问题逐渐暴露出来:一方面ES服务器越来越多,投入的成本越来越高;另一方面用户的满意度不高,日志写入延迟、查询慢甚至查不出来的问题一直困扰着用户;而从运维人员的角度看,ES的运维成本较高,运维的压力越来越大。 1.为什么选择ClickHouse ClickHouse是一款高性能列式分布式数据库管理系统,我们对ClickHouse进行了测试,

天天新知 2782

日语五十音图速记攻略:从形象联想法到实战训练

语言学习中的记忆编码与条件反射构建是高效掌握基础的关键。其核心原理在于通过将抽象符号与具象图像、故事或已知概念关联,利用大脑对视觉和叙事信息的天然高记忆效率,从而降低认知负荷,加速识别与反应速度。这一方法的技术价值在于将机械重复转化为意义联结,显著提升长期记忆的牢固度与提取速度。在日语学习这一具体应用场景中,五十音图作为表音文字系统,正是实践这一记忆技术的绝佳对象。通过系统化的形象联想、分组对比(如平假名与片假名的对应关系)以及间隔重复的实战训练,学习者可以高效攻克这一入门难关,为后续的词汇、语法学习铺设坚

weixin_29032489的博客 378

ClickHouse的文本处理与全文搜索

1.背景介绍 1. 背景介绍 ClickHouse 是一个高性能的列式数据库,主要用于实时数据处理和分析。它的核心特点是高速查询和高吞吐量,适用于实时数据分析、日志分析、实时监控等场景。ClickHouse 支持多种数据类型,包括文本数据,因此可以进行文本处理和全文搜索。 在本文中,我们将深入探讨 ClickHouse 的文本处理与全文搜索功能,涵盖其核心概念、算法原理、最佳实践、应用场景等...

AI天才研究院 1629

【异常】飞书OpenClaw机器人 HTTP 401: Invalid Authentication 报错排查与解决方案

HTTP 401 是HTTP协议标准定义的未授权(Unauthorized)状态码,核心含义为请求方身份认证无效,服务端拒绝执行本次请求。飞书开放平台服务端对自建机器人的全链路鉴权校验失败。无论是机器人接收飞书事件推送的上行请求,还是机器人主动调用飞书开放平台API的下行请求,只要身份凭证无效、鉴权逻辑校验不通过,飞书服务端就会返回该报错,并最终透传到飞书客户端会话窗口中。

奶爸的编程之路,也就一周冷个三天 3761

AI摄影师:自动构图与修图算法设计

AI摄影师:自动构图与修图算法设计​ ,人工智能,计算机视觉,大模型,AI,AI 摄影师的核心能力体现在两方面:自动构图(通过算法优化画面元素布局)与智能修图(自动调整光影、色彩、细节)。例如,手机摄影中的 “AI 构图建议” 功能可实时框选最佳拍摄区域,修图软件中的 “一键增强” 能在 0.1 秒内完成专业级调色,这些都依赖于计算机视觉与深度学习的深度融合。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 16万+

ClickHouse 快速入门

文章目录一、ClickHouse入门1. ClickHouse的特点1.1 列式存储1.2 DBMS的功能1.3 多样化引擎1.4 高吞吐写入能力1.5 数据分区与线程级并行二、ClickHouse的安装1. 准备工作1. 1 确定防火墙处于关闭状态1.2 CentOS取消打开文件数限制1.3 安装依赖1.4 取消SELINUX2. 单机安装(在线)三、数据类型1. 整型2. 浮点型3. 布尔型4. Decimal 型5. 字符串6. 枚举类型7. 时间类型8. 数组四、表引擎1. 表引擎的使用2. Tin

mengxianglong123的博客 1万+

华为OD机试 - 去除多余空格(Python/JS/C/C++ 2024 E卷 100分)

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。去除文本多余空格,但不去除配对单引号之间的多余空格。给出关键词的起始和结束下标,去除多余空格后刷新关键词的起始和结束下标。条件约束:输入为两行字符串:第一行:待去除多余空格的文本,用例保证如果有单引号,则单引号成对出现,且单引号可能有多对。 第二

学Java,找哪吒 1216

ClickHouse 和 列式存储:MergeTree 原理快速入门

列式存储 和 MergeTree 引擎。通过对比行式与列式数据库,理解它们在数据压缩、查询效率等方面的差异;并结合示例深入浅出地解释 ClickHouse 如何通过稀疏索引、后台合并、TTL机制等手段,实现“写多读快”的 OLAP 查询体验。

weixin_38021062的博客 1025

YOLO26全网首发:CVPR2026 MixerCSeg | DEGConv方向引导边缘门控,破解细长裂缝检测难题

DEGConv通过方向先验嵌入、边缘增强卷积和动态门控三大机制,针对性解决了YOLO在处理裂缝类细长、多方向、低对比度目标时的特征提取不足问题,显著提升了裂缝检测的精度与鲁棒性。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1034

实战|2026美团&大众点评餐饮数据抓取:Playwright/Selenium反爬全攻略

核心结论:2026年美团&大众点评的反爬核心是“多维度风控”,需组合使用“住宅IP+设备指纹伪装+真人行为模拟+动态验证处理”,Playwright是最优工具,Selenium仅作为补充。效率优化:单机爬取速度建议控制在“每小时500-800条”,避免高频请求触发风控;分布式爬取需确保每个节点使用独立住宅IP,且行为轨迹不一致。扩展方向集成WASM解密工具,破解2026年美团的Token加密;用AI识别动态点选验证,提升验证通过率;对接大数据平台,对抓取的餐饮数据进行可视化分析。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2462

Web 架构之日志系统:从 ELK 到 ClickHouse

在 Web 架构中,日志系统是非常重要的组成部分。它可以帮助开发者和运维人员监控系统的运行状态、排查问题、进行数据分析等。常见的日志系统架构有 ELK 和 ClickHouse,下面我们将分别介绍这两种架构。ClickHouse 是一个开源的列式数据库管理系统,它专门用于在线分析处理(OLAP)场景。它具有高性能、高可扩展性和低延迟等特点,非常适合处理大规模的日志数据。ELK 在实时搜索和简单查询方面性能较好,但在处理大规模数据和复杂查询时性能较差。

懂搬砖 864

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

ClickHouse 与 Quickwit 集成实现高效查询

在当今大数据分析领域,ClickHouse作为一款高性能的列式数据库,以其出色的查询速度和对大规模数据的处理能力,广泛应用于在线分析处理 (OLAP) 场景。ClickHouse 的列式存储和并行计算能力使得它在处理结构化数据查询时极具优势,尤其是在对日志、指标、事件等数据进行统计分析时表现突出。然而,在面对全文检索(特别是针对非结构化数据的复杂查询)时,ClickHouse 原生的功能支持有限。比如,涉及文本内容的深度搜索(如关键词搜索、模糊匹配等)可能无法充分发挥 ClickHouse 的优势。

hello.reader 1799

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

ClickHouse 列式数据库的 “性能王者”

ClickHouse是一款面向OLAP的高性能列式SQL数据库,由俄罗斯Yandex团队为解决海量数据实时分析需求而开发。其核心优势在于列式存储、向量化执行、分布式架构和近实时写入能力,相比传统行式数据库在分析查询速度上提升数十倍。ClickHouse适用于日志分析、实时报表、用户行为分析等读多写少场景,但不适合高频事务处理。与Hive、SparkSQL等工具相比,ClickHouse在"海量数据+低延迟"场景中表现突出,是构建高效数据分析系统的理想选择。

x² 1369

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

ClickHouse架构和内部原理

ClickHouse是一个高性能分布式列式数据库管理系统,用于快速处理大规模数据集。它采用了一种分布式、可扩展的架构,具有高吞吐量和低延迟的特点。下面将详细介绍ClickHouse的架构和内部原理。下面是一些常用的ClickHouse参数介绍和一个完整的代码案例:参数介绍在ClickHouse中,有很多参数可以用来配置和优化系统的性能和功能。下面是一些常用的参数介绍:max_memory_usage:指定每个查询的最大内存使用量。max_threads:指定每个查询的最大线程数。

互联网知识分享 1024

列式数据库ClickHouse,大宽表聚合、报表一下全搞定

关系型数据库( MySQL、Oracle、PostgreSQL)非关系型数据库(Redis、MongoDB、Cassandra、Neo4j)全文搜索引擎和分布式文档存储系统(Elasticsearch )列式数据库(ClickHouse)每种都在各自的领域表现出色,但当涉及到大规模数据分析和复杂查询时,ClickHouse 出现在了舞台上。ClickHouse使用列式存储,这意味着它可以高效地执行聚合、过滤和排序操作。面试经常问到大宽表查询聚合怎么办,这不是解决方案来了嘛!!

小王博客基地 3366

ClickHouse 原理:如何为列式存储构建快速 UPDATE I:特别设计的专用引擎

ClickHouse通过将更新操作转化为插入新数据行的方式,巧妙地规避了列式存储中行级更新的性能瓶颈。本文介绍了ReplacingMergeTree等专为高效更新设计的引擎原理:通过后台合并机制自动保留最新版本数据,实现高吞吐写入与更新。这种"插入代替更新"的架构充分利用ClickHouse强大的并发插入能力,使其在大规模数据场景下仍能保持卓越性能。文章还详细解析了数据分片的结构与合并机制,为理解ClickHouse独特的更新实现方式奠定基础。

SmartSi 934

ClickHouse 介绍:深度解析高性能列式数据库的核心优势

ClickHouse 作为一款高性能的列式数据库,以其出色的性能、灵活的架构和丰富的功能,成为大数据分析领域的重要工具。其列式存储、数据压缩、高性能查询、实时分析、分布式架构、SQL 支持以及与其他工具的兼容性,使其在处理海量数据时表现卓越。 如果你的工作涉及到大规模数据处理和分析,ClickHouse 是一个值得考虑的解决方案。它能够帮助你快速获取数据洞察,优化存储成本,并确保系统的高效和稳定。

weixin_37600397的博客 1886

开源列式分布式数据库clickhouse

ClickHouse 是一个开源列式数据库管理系统,专为在线分析处理(OLAP)而设计。它能够快速执行高并发、高效的数据查询,适合用于处理大量数据。

西京刀客 1455

中间件--ClickHouse-5--架构设计(分布式架构,列式压缩存储、并行计算)

ClickHouse的列式存储、向量化执行引擎和分布式架构 是其高性能的核心。通过 数据分块压缩、MPP 并行计算 和 ZooKeeper 协调,它在海量数据分析场景中实现了 秒级响应 和 线性扩展能力。其架构设计特别适合读多写少的场景,但需注意避免频繁更新/删除操作(因其追加写入模式)。逆风翻盘,Dare To Be!!!

weisian的博客 3万+

我们是如何为 ClickHouse 列式存储构建快速 UPDATE 的 Part1/3:特别设计的专用引擎

ClickHouse 已经提供了 UPDATE 所应具备的一切能力——只是以它特有的高性能方式实现。

ClickHouseDB的博客 1142
上一篇: 别了磁盘:ClickHouse Cloud 中无状态计算背后的架构
下一篇: 我们是如何为 ClickHouse 列式存储构建快速 UPDATE 的 Part1/3:特别设计的专用引擎
ClickHouseDB
博客等级 码龄3年 1707粉丝 320原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值