深入剖析 Doris 倒排索引(上):原理与应用全解析
大数据处理与分析的场景中,快速且精准的查询能力是衡量数据系统优劣的关键指标之一。Apache Doris 的倒排索引功能,就像一把 “利刃”,为数据检索与分析效率的提升提供了强大支持。本文上篇将围绕 Doris 倒排索引的基础原理与功能特性展开,带你深入了解这一技术的核心要点。
一、倒排索引基础:构建数据检索的基石
(一)倒排索引文件:存储结构的奥秘
倒排索引是针对 Doris 中 segment 级别的数据文件构建的,其核心作用是将数据中的关键词与包含该关键词的文档建立关联,从而实现高效检索。在 Doris 中,倒排索引文件存在两种类型:V1 和 V2,不同版本对默认索引文件类型的设置有所差异。
-
版本差异与配置:Doris 3.0 版本之前,默认使用 V1 类型的倒排索引文件;从 3.0 版本开始,默认切换为 V2 类型。在 3.0 和 2.1 版本中,用户可以通过配置
properties inverted_index_storage_format=v1/v2
来灵活选择索引文件类型,但需要注意的是,Doris 2.0 版本并不支持此配置。 -
文件结构对比:以实际文件存储为例,V1 版本的索引文件结构较为分散,在数据存储目录下,一个数据段可能对应多个.idx 文件。例如:
└── [4.0K] 11991└── [ 36K] 2110008962├── [ 78M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12129.idx├── [8.6M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12131.idx├── [ 19M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12133.idx├── [ 33K] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12135.idx├── [6.1M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12137.idx├── [ 27M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12139.idx├── [ 13M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12141.idx├── [ 55K] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12143.idx├── [2.4M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12145.idx├── [2.5M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12147.idx├── [2.7M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0_12149.idx├── [375M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0.dat
而 V2 版本对文件进行了整合优化,减少了文件数量,例如:
└── [4.0K] 11991└── [ 36K] 2110008962├── [178M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0.idx├── [375M] 020000000000016a1c464dcb7093448cbaff369795c7e79b_0.dat
这种结构上的差异,使得 V2 版本在存储和查询性能上通常更具优势。
(二)支持与受限类型:明确应用边界
倒排索引并非适用于所有数据类型,了解其支持和受限类型,有助于我们在实际应用中合理使用。
-
支持类型:
-
文本类型:CHAR、STRING、VARCHAR、TEXT 等文本类型,是倒排索引发挥全文检索和查询加速功能的 “主战场”。例如,在日志分析场景中,对包含大量文本信息的日志内容字段建立倒排索引,能够快速定位包含特定关键词的日志记录。
-
数组类型:ARRAY类型可用于特定函数的查询加速,如
array_contains
(判断数组是否包含某个元素)、array_overlaps
(判断两个数组是否有重叠元素)等函数。假设存在一个存储用户兴趣标签的数组字段,通过倒排索引可以加速查询包含特定兴趣标签的用户数据。 -
数值与日期类型:INT、BIGINT、DECIMAL 等数值类型,以及 DATE、DATEV2、DATETIME、DATETIMEV2 等日期类型,倒排索引能够有效提升基于这些类型字段的查询速度,如数值范围查询和日期筛选查询。
-
-
受限类型:
-
浮点类型:FLOAT、DOUBLE 等浮点类型目前暂不支持倒排索引,后续会考虑开放支持。
-
复杂类型:MAP、STRUCT、JSON、HLL、BITMAP、QUANTILE_STATE、AGG_STATE 等复杂数据类型,由于结构复杂,暂不适合使用倒排索引。
-
表模型限制:在 AGGREGATE 表模型和未开启 MOW(Materialized on Write)的 UNIQUE 表模型中,倒排索引只能应用于 Key 列,非 Key 列无法建立倒排索引。
-
二、全文检索:精准匹配的强大武器
(一)索引构建:精细配置的艺术
在 Doris 中创建全文索引,需要在建表语句中添加特定语法:
CREATE TABLE table_name
(column_name1 TYPE1,column_name2 TYPE2,column_name3 TYPE3,INDEX idx_name1(column_name1) USING INVERTED [PROPERTIES(...)] [COMMENT 'your comment'],INDEX idx_name2(column_name2) USING INVERTED [PROPERTIES(...)] [COMMENT 'your comment']
)
table_properties;
其中,PROPERTIES
是全文检索模式下的必选项,通过它可以对索引进行多种精细配置:
-
parser(分词器):分词器决定了文本的分词方式,不同的分词器适用于不同的语言和场景。
- 不指定分词器:表示不对文本进行分词处理,适用于不需要分词的特殊场景。
- english(英文分词):适用于被索引列为英文的情况,它会使用空格和标点符号进行分词,性能较高。例如,对于英文文档中的句子 “Hello, world! This is a test.”,会被分词为 “Hello”、“world”、“This”、“is”、“a”、“test”。
- chinese(中文分词):专为中文设计,适用于以中文为主的字段。不过,由于中文分词的复杂性,其性能相对低于 english 分词器。例如,“我爱北京天安门” 可能会被分词为 “我”、“爱”、“北京”、“天安门”。
- unicode(多语言混合分词):能够处理中英文混合、多语言混合的文本内容,支持邮箱、IP 地址、字符数字混合内容的分词,也支持中文按字符分词。例如,对于文本 “我的邮箱是 example@example.com,IP 地址是 192.168.1.1”,能够准确分词并建立索引。
-
parser_mode(中文分词模式):该属性仅在
parser = chinese
时生效,用于调整中文分词的粒度。- fine_grained(细粒度):会分出更多、更短的词。例如,“武汉市长江大桥” 会被分成 “武汉”、“武汉市”、“市长”、“长江”、“长江大桥”、“大桥” 等 6 个词。
- coarse_grained(粗粒度):分出的词更少、更长,“武汉市长江大桥” 会被分成 “武汉市”、“长江大桥” 2 个词。默认情况下,中文分词采用粗粒度模式。
-
support_phrase(是否支持短语查询加速):
- true:开启短语查询加速功能,能够支持
MATCH_PHRASE
算子进行短语查询,但会使索引体积增大。 - false:不支持短语查询,有助于节省存储空间,默认值为 false。
- true:开启短语查询加速功能,能够支持
-
char_filter(文本预处理):用于指定文本在分词前的预处理行为。
- char_filter_type:目前仅支持
char_replace
类型。 - char_filter_pattern:指定需要被替换的字符,如 “.” 和 “” 或者 “.” ,需要注意的是,这里的字符只能是 ascii 码,不能使用其他 unicode 字符。
- char_filter_replacement:指定替换成的字符,默认是空格。
- char_filter_type:目前仅支持
-
ignore_above(限制索引长度):设置未分词字符串索引的最大长度,长度超过该值的字符串将不会被索引。对于字符串数组,该限制分别作用于每个元素,默认值为 256 字节。
-
lower_case(是否转换为小写):
- true:将文本转换为小写,方便进行忽略大小写的匹配操作,在 2.0.7 和 2.1.2 版本之后默认值为 true。
- false:不进行小写转换,这是旧版本的默认设置。
-
stopwords(停用词配置):默认停用词表包含如 “is”、“the”、“a” 等无意义词汇,在写入和查询时会忽略这些词。将其设置为 none 时,表示不使用停用词表。
(二)检索算子:多样匹配的实现
Doris 提供了丰富的全文索引检索算子,以满足不同场景下的查询需求:
-
MATCH_ANY:用于匹配包含任一关键词的文档。例如,执行查询语句
SELECT * FROM table_name WHERE content MATCH_ANY 'keyword1 keyword2';
,会返回content
列中包含keyword1
或keyword2
中至少一个的所有行。也就是说,只要文档中出现了keyword1
或者keyword2
,或者两者都出现,都会被匹配出来。 -
MATCH_ALL:用于匹配同时包含所有关键词的文档。如
SELECT * FROM table_name WHERE content MATCH_ALL 'keyword1 keyword2';
,只有content
列中同时出现keyword1
和keyword2
的行才会被返回。 -
MATCH_PHRASE:实现短语匹配,要求关键词按顺序相邻出现。例如,执行
SELECT * FROM table_name WHERE content MATCH_PHRASE 'keyword1 keyword2';
,只有像 “keyword1 keyword2”、“wordx keyword1 keyword2”、“wordx keyword1 keyword2 wordy” 这样keyword2
紧跟在keyword1
后面的文档才能匹配成功。需要注意的是,使用该算子必须在PROPERTIES
中开启"support_phrase" = "true"
。 -
MATCH_PHRASE slop:松散短语匹配,允许关键词之间存在一定间隔,间隔词数不超过指定的
slop
值。例如,SELECT * FROM table_name WHERE content MATCH_PHRASE 'keyword1 keyword2 ~3';
,当slop=3
时,“keyword1 keyword2”、“keyword1 a keyword2”、“keyword1 a b c keyword2” 等都能匹配,因为keyword1
和keyword2
中间隔的词分别是 0、1、3,都不超过 3。此外,当slop > 0
时,不再要求keyword1
和keyword2
的顺序固定,像 “keyword2 a b c keyword1” 也能匹配。如果使用正号 “+”,则会进入顺序严格模式,要求关键词必须按指定顺序出现 。 -
MATCH_PHRASE_PREFIX:短语匹配,但最后一个词允许进行前缀匹配。例如,
SELECT * FROM table_name WHERE content MATCH_PHRASE_PREFIX 'keyword1 keyword';
,“keyword1 keyword2abc”、“keyword1 keyword2” 能匹配,因为 “keyword2abc” 和 “keyword2” 都满足 “keyword” 的前缀匹配条件,而 “keyword1 keyword3” 则无法匹配。当只指定一个词进行查询时,如SELECT * FROM table_name WHERE content MATCH_PHRASE_PREFIX 'keyword1';
,会退化为该词的前缀匹配模式。 -
MATCH_REGEXP:进行正则匹配查询,需要注意的是,这里的查询词不会进行分词处理。例如,
SELECT * FROM table_name WHERE content MATCH_REGEXP '^key_word.*';
,会匹配任何以 “key_word” 开头的词,像 “key_word”、“key_words” 等,但 “keyword” 不会匹配,即使 “_” 是停用词。 -
MATCH_PHRASE_EDGE:对标 ES 的 query_string,实现边缘短语匹配,用于匹配以指定短语作为完整单词边界的文档。例如,有以下几行数据(字段名为
content
):- “The quick brown fox jumps over the lazy dog.”
- “Spotlight on new lighthouse project.”
- “Research shows search engine optimization is key.”
- “Doris is a powerful SQL database.”
当执行查询
当执行查询content MATCH_PHRASE_EDGE'search engine optim'
时: - 第一个词 “search” 被视为后缀词,会匹配 “research” 和 “search”;
- 中间词 “engine” 要求精确匹配;
- 最后一个词 “optim” 被视为前缀词,会匹配 “optimization”;
- 并且要求这三个词紧挨着出现。所以,只有文档 3“Research shows search engine optimization is key.” 会被匹配出来。
三、查询加速:提升检索效率的核心
(一)支持的运算符和函数:加速的范围
倒排索引能够对多种运算符和函数的查询起到加速作用,同时与 BloomFilter 索引、NGram BloomFilter 索引在支持的运算符上存在差异:
运算符 / 函数 | 倒排索引 | BloomFilter 索引 | NGram BloomFilter 索引 |
---|---|---|---|
= | YES | YES | NO |
!= | YES | NO | NO |
IN | YES | YES | NO |
NOT IN | YES | NO | NO |
>, >=, <, <=, BETWEEN | YES | NO | NO |
IS NULL | YES | NO | NO |
IS NOT NULL | YES | NO | NO |
LIKE | NO | NO | YES |
array_contains | YES | NO | NO |
array_overlaps | YES | NO | NO |
is_ip_address_in_range | YES | NO | NO |
例如,在进行数值范围查询SELECT * FROM table WHERE age > 18;
时,倒排索引可以快速过滤出符合条件的行;使用array_contains
函数查询SELECT * FROM table WHERE tags ARRAY_CONTAINS 'sports';
,倒排索引同样能加速查询过程。不过,需要注意的是,LIKE
运算符需要通过 NGram BloomFilter 索引加速,并且要在在FE开启set enable_function_pushdown = true
(二)倒排索引加速查询的原理
倒排索引加速查询的核心逻辑,是通过提前过滤掉不需要加载的行,大幅减少数据读取时的 I/O 以及 CPU 资源消耗。比如在一张存储用户信息的大表中,若对 “用户标签” 字段建立倒排索引,当执行SELECT * FROM users WHERE user_tags MATCH_ANY '文字1,文字2'
查询时,倒排索引能迅速定位到包含 “文字1” 或 “文字2” 标签的用户所在行,无需扫描全表数据,直接跳过无关行,从而显著提升查询效率。
通过上篇的介绍,相信你已经对 Doris 倒排索引的原理与应用有了较为全面的认识。然而在实际使用过程中,难免会遇到各种问题。下篇将聚焦倒排索引常见问题,为你提供详细的解决方案,助你轻松应对使用中的挑战,敬请期待!