全文倒排索引
自 3.3.0 版本起,StarRocks 支持全文倒排索引,该索引可以将文本分解为更小的词,并为每个词创建一个索引条目,该条目可以显示词与数据文件中相应行号之间的映射关系。对于全文搜索,StarRocks 会根据搜索关键字查询倒排索引,快速定位与关键字匹配的数据行。
Primary Key 表和 Shared-data 集群尚不支持全文倒排索引。
概述
StarRocks 将底层数据存储在按列组织的数据文件中。每个数据文件都包含基于索引列的全文倒排索引。索引列中的值被标记化为单个词。标记化后的每个词都被视为一个索引条目,映射到该词出现的行号。当前支持的标记化方法包括英语标记化、中文标记化、多语言标记化和无标记化。
例如,如果一个数据行包含 “hello world”,其行号为 123,则全文倒排索引会基于该标记化结果和行号构建索引条目:hello->123,world->123。
在全文搜索期间,StarRocks 可以使用全文倒排索引定位包含搜索关键字的索引条目,然后快速找到关键字出现的行号,从而显著减少需要扫描的数据行数。
基本操作
创建全文倒排索引
在创建全文倒排索引之前,您需要启用 FE 配置项 enable_experimental_gin
。
ADMIN SET FRONTEND CONFIG ("enable_experimental_gin" = "true");
此外,全文倒排索引只能在 Duplicate Key 表中创建,并且表属性 replicated_storage
需要为 false
。
在创建表时创建全文倒排索引
在列 v
上创建具有英语标记化的全文倒排索引。
CREATE TABLE `t` (
`k` BIGINT NOT NULL COMMENT "",
`v` STRING COMMENT "",
INDEX idx (v) USING GIN("parser" = "english")
) ENGINE=OLAP
DUPLICATE KEY(`k`)
DISTRIBUTED BY HASH(`k`) BUCKETS 1
PROPERTIES (
"replicated_storage" = "false"
);
parser
参数指定标记化方法。支持的值和描述如下none
(默认):不进行标记化。在构造全文倒排索引时,索引列中的整行数据被视为单个索引项。english
:英语标记化。此标记化方法通常在任何非字母字符处进行标记化。此外,大写英文字母会转换为小写。因此,查询条件中的关键字需要是小写英文而不是大写英文,才能利用全文倒排索引来定位数据行。chinese
:中文标记化。此标记化方法使用 CLucene 中的 CJK Analyzer 进行标记化。standard
:多语言标记化。此标记化方法提供基于语法的标记化(基于 Unicode 文本分割算法),并且适用于大多数语言和混合语言的情况,例如中文和英文。例如,此标记化方法可以区分中文和英文在两种语言共存时。在标记化英文后,它会将大写英文字母转换为小写。因此,查询条件中的关键字需要是小写英文而不是大写英文,才能利用全文倒排索引来定位数据行。
- 索引列的数据类型必须为 CHAR、VARCHAR 或 STRING。
在创建表后添加全文倒排索引
创建表后,您可以使用 ALTER TABLE ADD INDEX
或 CREATE INDEX
添加全文倒排索引。
ALTER TABLE t ADD INDEX idx (v) USING GIN('parser' = 'english');
CREATE INDEX idx ON t (v) USING GIN('parser' = 'english');
管理全文倒排索引
查看全文倒排索引
执行 SHOW CREATE TABLE
以查看全文倒排索引。
MySQL [example_db]> SHOW CREATE TABLE t\G
删除全文倒排索引
执行 ALTER TABLE ADD INDEX
或 DROP INDEX
以删除全文倒排索引。
DROP INDEX idx on t;
ALTER TABLE t DROP index idx;
通过全文倒排索引加速查询
创建全文倒排索引后,您需要确保系统变量 enable_gin_filter
已启用,以便倒排索引可以加速查询。此外,您需要考虑是否对索引列值进行了标记化,以确定哪些查询可以被加速。
当索引列被标记化时支持的查询
如果全文倒排索引对索引列进行标记化,即 'parser' = 'standard|english|chinese'
,则只有 MATCH
谓词支持使用全文倒排索引进行数据过滤,格式需要为 <col_name> (NOT) MATCH '%keyword%'
。keyword
必须是字符串字面量,不支持表达式。
-
创建一个表并插入几行测试数据。
CREATE TABLE `t` (
`id1` bigint(20) NOT NULL COMMENT "",
`value` varchar(255) NOT NULL COMMENT "",
INDEX gin_english (`value`) USING GIN ("parser" = "english") COMMENT 'english index'
)
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH(`id1`)
PROPERTIES (
"replicated_storage" = "false"
);
INSERT INTO t VALUES
(1, "starrocks is a database
1"),
(2, "starrocks is a data warehouse"); -
使用
MATCH
谓词进行查询。
-
查询
value
列包含关键字starrocks
的数据行。MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "starrocks";
-
检索
value
列包含以data
开头的关键字的数据行。MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "data%";
注意
-
在查询期间,可以使用
%
模糊匹配关键字,格式为%keyword%
。但是,关键字必须包含单词的一部分。例如,如果关键字为starrocks
,则它无法匹配单词starrocks
,因为它包含空格。MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "star%";
+------+-------------------------------+
| id1 | value |
+------+-------------------------------+
| 1 | starrocks is a database1 |
| 2 | starrocks is a data warehouse |
+------+-------------------------------+
2 rows in set (0.02 sec)
MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "starrocks ";
Empty set (0.02 sec) -
如果使用英语或多语言标记化来构造全文倒排索引,则大写英文单词在实际存储全文倒排索引时会转换为小写。因此,在查询期间,关键字需要是小写而不是大写,才能利用全文倒排索引来定位数据行。
MySQL [example_db]> INSERT INTO t VALUES (3, "StarRocks is the BEST");
MySQL [example_db]> SELECT * FROM t;
+------+-------------------------------+
| id1 | value |
+------+-------------------------------+
| 1 | starrocks is a database |
| 2 | starrocks is a data warehouse |
| 3 | StarRocks is the BEST |
+------+-------------------------------+
3 rows in set (0.02 sec)
MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "BEST"; -- Keyword is uppercase English
Empty set (0.02 sec) -- Returns an empty result set
MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "best"; -- Keyword is lowercase English
+------+-----------------------+
| id1 | value |
+------+-----------------------+
| 3 | StarRocks is the BEST | -- Can locate data rows that meet the condition
+------+-----------------------+
1 row in set (0.01 sec) -
查询条件中的
MATCH
谓词必须用作下推谓词,因此它必须在 WHERE 子句中,并且针对索引列执行。以下面的表和测试数据为例
CREATE TABLE `t_match` (
`id1` bigint(20) NOT NULL COMMENT "",
`value` varchar(255) NOT NULL COMMENT "",
`value_test` varchar(255) NOT NULL COMMENT "",
INDEX gin_english (`value`) USING GIN("parser" = "english") COMMENT 'english index'
)
ENGINE=OLAP
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH (`id1`) BUCKETS 1
PROPERTIES (
"replicated_storage" = "false"
);
INSERT INTO t_match VALUES (1, "test", "test");以下查询语句不符合要求
-
由于查询语句中的
MATCH
谓词不在 WHERE 子句中,因此无法下推,导致查询错误。MySQL [test]> SELECT value MATCH "test" FROM t_match;
ERROR 1064 (HY000): Match can only be used as a pushdown predicate on a column with GIN in a single query. -
由于查询语句中执行
MATCH
谓词的列value_test
不是索引列,因此查询失败。MySQL [test]> SELECT * FROM t_match WHERE value_test match "test";
ERROR 1064 (HY000): Match can only be used as a pushdown predicate on a column with GIN in a single query.
-
当索引列未被标记化时支持的查询
如果全文倒排索引不对索引列进行标记化,即 'parser' = 'none'
,则可以使用以下查询条件中的所有下推谓词,通过全文倒排索引进行数据过滤
-
表达式谓词:(NOT) LIKE, (NOT) MATCH
注意- 在这种情况下,
MATCH
在语义上等同于LIKE
。 MATCH
和LIKE
仅支持格式(NOT) <col_name> MATCH|LIKE '%keyword%'
。keyword
必须是字符串字面量,不支持表达式。请注意,如果LIKE
不满足此格式,即使查询可以正常执行,它也会降级为不使用全文倒排索引来过滤数据的查询。
- 在这种情况下,
-
常规谓词:
==
,!=
,<=
,>=
,NOT IN
,IN
,IS NOT NULL
,NOT NULL
如何验证全文倒排索引是否加速查询
执行查询后,您可以查看 Query Profile 的 scan 节点中的详细指标 GinFilterRows
和 GinFilter
,以查看使用全文倒排索引过滤的行数和过滤时间。