倒排索引

倒排索引原理介绍

  1. 基本概念 倒排索引由两部分组成:
  • 词典(Dictionary):存储所有文档集合中出现过的唯一单词(或短语)的列表。
  • 倒排表(Posting List):对于词典中的每个单词,都有一个与之对应的倒排列表,记录了包含这个单词的所有文档的文档ID以及单词在文档中出现的位置信息。
  1. 构建过程

    • 分词(Tokenization):将文档内容分割成单词或短语的过程。
    • 标准化(Normalization):对分词结果进行处理,如转小写、去除停用词、词干提取等。
    • 构建词典:将处理后的单词加入词典,并为每个单词分配一个唯一的ID。
    • 构建倒排列表:对于每个文档,记录下文档中所有单词的ID和位置信息,并将这些信息与词典中的单词ID关联起来,形成倒排列表。
  2. 查询过程 当用户提交一个查询请求时,搜索引擎会执行以下步骤:

    • 查询解析:将用户的查询语句分解成单词或短语。
    • 查找词典:在词典中查找这些单词或短语的ID。
    • 检索倒排列表:根据单词ID,从倒排列表中检索包含这些单词的所有文档ID。
    • 合并结果:根据查询语句中的逻辑(如AND、OR、NOT等),合并不同单词的检索结果,确定最终的文档列表。
  3. 应用场景 倒排索引主要用于全文搜索领域,如搜索引擎、文档检索系统等,它能够快速响应用户的查询请求,提供高效的检索服务。

功能介绍

Table 的一行对应一个到Elasticsearch中的文档、一列对应文档中的一个字段,因此利用倒排索引可以根据关键词快速定位包含它的行,达到 WHERE 子句加速的目的。同时如上面原理介绍词典和倒排表也会占用Lakehouse的存储,因此您会看到数据大小比没有倒排索引的表高

  1. 支持了字符串类型的全文检索,支持了match_all、match_any、match_phrase、match_phrase_prefix、match_regexp函数。性能会比like更快,目前Lakehouse在部分场景下like也会使用倒排索引,但是推荐您使用全文检索函数

  2. 加速普通等值、范围查询

    • 支持字符串、数值类型的 =, !=, >, >=, <, <= 快速过滤
  3. 支持完善的逻辑组合

    • 支持AND、OR、NOT下推
  4. 灵活、快速的索引管理

    • 创建倒排索引:支持在创建表上定义倒排索引,支持在已有的表上增加倒排索引,如果是在已有表创建的索引,对存量数据也需要添加倒排索引。可以执行构建索引命令
    • 构建倒排索引:对存量数据也需要添加倒排索引,构建索引会启动一个SQL任务,需要消耗计算资源
    • 列出所有倒排索引:查看一张表上的所有倒排索引
    • 查看倒排索引详情:查看索引的详情包括大小等
    • 删除倒排索引:执行drop index会立即成功,会删除掉index的元数据信息。但是不会立即删除索引存储信息。
  5. 倒排索引目前支持的数据类型:

  • 数值类型 (TINYINT、SMALLINT、INT、FLOAT、DOUBLE)
  • 日期类型(DATE、TIMESTAMP)
  • 字符串类型 (STRING、VARCHAR、CHAR) 当倒排索引字段为字符串类型时,您需要指定分词,以及分词模式。目前支持的分词类型为:
    • keyword: 此类型的字段不会进行分词。整个字符串被视为单一的词根,并存储在倒排索引中。搜索时,需要提供完整的字段值以实现精确匹配。
    • english: 专为英文设计的分词器,仅识别连续的ASCII字母和数字,并将文本统一转换为小写。适合处理英文内容,忽略非字母数字字符。
    • chinese: 中文文本分词插件,识别中文和英文字符,同时过滤掉标点符号,并将英文字母转换为小写。适用于中英文混合文本的处理。
    • unicode: 基于Unicode文本分割算法的分词器,能够识别多种语言的文本边界,有效将文本分割成单词,并将字母转换为小写。适合多语言环境下的文本处理。
  1. 分词模式:参数支持分词模式参数 mode,包括:

mode = "max_word":最细粒度分词模式

mode = "smart":智能分词模式

您可以使用tokenize(input[, option])函数来测试分词。使用tokenize测试分词样例如下:

--使用keword分词
SELECT TOKENIZE('Lakehouse的倒排索引',map('analyzer', 'keyword', "mode", "max_word")) as toke;
+--------------------+
|        toke        |
+--------------------+
| ["lakehouse的倒排索引"] |
+--------------------+
--使用chinese分词
SELECT TOKENIZE('中文分词是自然语言处理的基础模块。',map('analyzer', 'chinese')) as toke;
+--------------------------------+
|              toke              |
+--------------------------------+
| ["中文","分词","是","自然语言","处理","的","基础","模块"]|
+--------------------------------+
--使用unicode分词
SELECT TOKENIZE('中文分词是自然语言处理的基础模块。',map('analyzer', 'unicode',"mode", "max_word")) as toke;
+--------------------------------+
|              toke              |
+--------------------------------+
| ["中文","分词","是","自然","语言","处理","的","基础","模","块"] |
+--------------------------------+
--使用english分词
SELECT TOKENIZE('Lakehouse inverted index',map('analyzer', 'english')) as toke;
+----------------------------------+
|               toke               |
+----------------------------------+
| ["lakehouse","inverted","index"] |
+----------------------------------+

倒排索引无法优化场景

  • 在大多数情况下,倒排索引并不会显著提高执行时间为亚秒的查询的性能。
  • 不支持外部表
  • 不支持对表列进行了强制转化,比如
--可以查询加速的,表中数据类型string
where string_col='10086';
--对需要匹配的值进行转化
where string_col=cast(10086 as string);

--无法查询加速的,因为对表列进行了强制转化
where cast(string_col as int)=10086 ;

使用倒排索引查询

倒排索引函数

函数中要求分词和表保持一致否则无法使用倒排索引加速查询,analyzer支持auto参数,添加auto参数可以自动会映射表中的分词

函数名功能举例
tokenize(input[, option])分词,返回值为arraytokenize('a b', map('analyzer', 'english')) = ["a", "b"]
match_all(input, query[, option])匹配所有,先将query分词,再讲input分词,需要保证input的分词结果包含所有query分词结果,返回boolmatch_all('a b c', 'b a', map('analyzer', 'english')) = true
match_any(input, query[, option])匹配任意,input的分词结果包含query分词结果的任意元素即可,返回boolmatch_any('a b c', 'd a', map('analyzer', 'english')) = true
match_phrase(input, query[, option])匹配短语,在match_all的基础上,匹配的结果顺序要和query分词结果的顺序要一致且连续match_phrase('a b c', 'a b', map('analyzer', 'english')) = truematch_phrase('a b c', 'a c', map('analyzer', 'english')) = false
match_phrase_prefix(input, query[, option])匹配短语前缀,input的分词结果,前n-1一个匹配规则和match_phrase相同,最后一个元素符合前缀匹配match_phrase_prefix('a b cd', 'b c', map('analyzer', 'english')) = true
match_regexp(input, query[, option])匹配正则表达式,将input的分词结果,使用query的正则表达式匹配,有任意匹配即可match_regexp('a b cd', 'c.*', map('analyzer', 'english')) = true

具体案例

创倒排索引的表

create table bulkload_data(id int,data string,INDEX data_index (data) INVERTED PROPERTIES('analyzer'='unicode'));

* 全文检索

```SQL
--匹配所有
select * from bulkload_data where match_all(data,'Elfriede Heaney',map('analyzer', 'unicode'));
+------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                   data                                                                   |
+------------------------------------------------------------------------------------------------------------------------------------------+
| {"address":"Apt. 423 78018 Wisozk Meadow, West Marge, WV 16958","name":"Elfriede Heaney","email":"jamar.schoen@gmail.com"}               |
| {"address":"Suite 654 89305 Dan Drive, Haiview, AZ 55461","name":"Elfriede Heaney","email":"kristofer.upton@yahoo.com"}                  |
| {"address":"1133 Cartwright Orchard, Port Jonathon, UT 71589-4026","name":"Elfriede Heaney","email":"douglass.nitzsche@yahoo.com"}       |
| {"address":"115 Avery Mountains, New Elfriede, TN 83686-3466","name":"Clarence Heaney","email":"emmanuel.lockman@yahoo.com"}             |
| {"address":"Suite 342 631 Konopelski Hollow, East Chingview, UT 79212","name":"Elfriede Heaney DDS","email":"mikel.keebler@hotmail.com"} |
| {"address":"415 Elfriede Row, New Adriene, SC 90250","name":"Jefferson Heaney","email":"len.price@yahoo.com"}                            |
+------------------------------------------------------------------------------------------------------------------------------------------+
--匹配短语
select * from bulkload_data where match_phrase(data,'Elfriede Heaney',map('analyzer', 'unicode'));
+------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                   data                                                                   |
+------------------------------------------------------------------------------------------------------------------------------------------+
| {"address":"Apt. 423 78018 Wisozk Meadow, West Marge, WV 16958","name":"Elfriede Heaney","email":"jamar.schoen@gmail.com"}               |
| {"address":"Suite 654 89305 Dan Drive, Haiview, AZ 55461","name":"Elfriede Heaney","email":"kristofer.upton@yahoo.com"}                  |
| {"address":"1133 Cartwright Orchard, Port Jonathon, UT 71589-4026","name":"Elfriede Heaney","email":"douglass.nitzsche@yahoo.com"}       |
| {"address":"Suite 342 631 Konopelski Hollow, East Chingview, UT 79212","name":"Elfriede Heaney DDS","email":"mikel.keebler@hotmail.com"} |
+------------------------------------------------------------------------------------------------------------------------------------------+
  • 加速普通值查询
select * from bulkload_data where data='{"address":"Apt. 423 78018 Wisozk Meadow, West Marge, WV 16958","name":"Elfriede Heaney","email":"jamar.schoen@gmail.com"}'
+----------------------------------------------------------------------------------------------------------------------------+
|                                                            data                                                            |
+----------------------------------------------------------------------------------------------------------------------------+
| {"address":"Apt. 423 78018 Wisozk Meadow, West Marge, WV 16958","name":"Elfriede Heaney","email":"jamar.schoen@gmail.com"} |
+----------------------------------------------------------------------------------------------------------------------------+

倒排索引计费

  • 存储资源:倒排索引会在数据文件之外额外创建倒排索引文件,索引文件和数据文件将按照存储大小统一收集存储费用。

创建倒排索引

建表时定义索引

语法

CREATE TABLE table_name(
  columns_difinition,
  INDEX index_name (column_name) INVERTED [COMMENT '']  PROPERTIES('analyzer'='english|chinese|keyword|unicode', 'mode' = 'smart|max_word'),
  INDEX index_name (column_name) INVERTED [COMMENT '']  PROPERTIES('analyzer'='english|chinese|keyword|unicode', 'mode' = 'smart|max_word'),
...
  INDEX index_name (column_name) INVERTED [COMMENT '']  PROPERTIES('analyzer'='english|chinese|keyword|unicode', 'mode' = 'smart|max_word')
);

columns_difinition:定义表的字段信息,最后一个字段必须使用逗号隔开

INDEX:关键字

index_name:自定义index的名称

column_name:需要添加索引的字段名称

INVERTED:关键字,表示倒排索引

COMMENT:指定index的说明信息

PROPERTIES:指定INDEX的参数,支持的属性目前支持指定分词。数值和日期类型则不需要指定properties,如果是字符串类型要求必须指定分词

  • 分词:'analyzer'='english|chinese | keyword|unicode',用于指定文本分词的策略,适用于不同的语言和需求。
    • keyword: 此类型的字段不会进行分词。整个字符串被视为单一的词根,并存储在倒排索引中。搜索时,需要提供完整的字段值以实现精确匹配。
    • english: 专为英文设计的分词器,仅识别连续的ASCII字母和数字,并将文本统一转换为小写。适合处理英文内容,忽略非字母数字字符。
    • chinese: 中文文本分词插件,识别中文和英文字符,同时过滤掉标点符号,并将英文字母转换为小写。适用于中英文混合文本的处理。
    • unicode: 基于Unicode文本分割算法的分词器,能够识别多种语言的文本边界,有效将文本分割成单词,并将字母转换为小写。适合多语言环境下的文本处理。
  • 分词模式: 'mode' = 'smart|max_word',这两种模式将提升中文搜索的灵活性与准确性,适用于不同的搜索需求场景。'mode' = 'smart' 使用更智能的方式进行较粗粒度的分词,避免过度切分。适用于需要更高准确率的场景,如短文本匹配、搜索建议等。 'mode' = 'max_word' 尽可能多地将句子切分为细粒度的词语。适用于需要高召回率的场景,如全文搜索或复杂查询。

案例

CREATE TABLE inverted_index_test(
  id int,
  name string,
  INDEX id_index (id) INVERTED  ,
  INDEX name_index (name) INVERTED  PROPERTIES('analyzer'='keyword','mode' = 'smart|max_word')
);

已有的表增加倒排索引

语法

CREATE INVERTED INDEX [IF NOT EXISTS] index_name ON TABLE 
[schema].table_name(col_name)
[COMMENT 'comment']
PROPERTIES('analyzer'='english|chinese|keyword|unicode','mode' = 'smart|max_word')

INVERTED: 索引类型,倒排索引

index_name: 表名字,位于schema下,schema下索引名称不能重复

col_name:列名只支持单列

PROPERTIES:指定INDEX的参数,支持的属性目前支持指定分词。数值和日期类型则不需要指定properties,如果是字符串类型要求必须指定分词

说明

执行CREATE INDEX仅对新增数据有效,对已有数据进行索引请使用BUILD INDEX命令。

案例

CREATE TABLE inverted_index_test(
  id int,
  name string
);

CREATE  INVERTED INDEX  id_index ON TABLE 
public.inverted_index_test(name)
PROPERTIES('analyzer'='unicode')

使用倒排索引查询

构建索引

对存量数据添加倒排索引

语法

-- 语法 1,默认给全表的存量数据加上倒排索引
BUILD INDEX index_name ON [schema].table_name;
-- 语法 2,可指定partition,可指定一个或多个,支持=, !=, >, >=, <, <=
BUILD INDEX index_name ON table_name WHERE partition_name1 = '1' and partition_name2 = '2';
  • index_name:指定要添加倒排索引名称
  • 支持指定分区构建:可以指定一个或者多个

说明

执行BULD INDEX是一个同步任务,执行过程会消耗计算资源。查看进度可以通过Job Profile查看。

当分区表数据量较大时,建议以分区为粒度依次创建索引。

案例

BUILD INDEX bulkload_data_index ON public.bulkload_data ;

列出表上所有的倒排索引

命令用于列出指定表中已创建的倒排索引。

语法

SHOW INDEX FROM [schema].table_name;

案例

 show index from public.bulkload_data;
+---------------------+------------+
|     index_name      | index_type |
+---------------------+------------+
| bulkload_data_index | inverted   |
+---------------------+------------+

查看倒排索引详情

本命令用于列出指定表中已创建的倒排索引的详情,添加上extended关键字可以查看倒排索引大小。

语法

DESC INDEX [EXTENDED]  index_name;

案例

desc index bulkload_data_index;
+--------------------+-------------------------+
|     info_name      |       info_value        |
+--------------------+-------------------------+
| name               | bulkload_data_index     |
| creator            | system_admin            |
| created_time       | 2024-05-27 16:11:23.928 |
| last_modified_time | 2024-05-27 16:11:23.928 |
| comment            |                         |
| index_type         | inverted                |
| table_name         | bulkload_data           |
| table_column       | data                    |
+--------------------+-------------------------+

desc index extended  bulkload_data_index;
+--------------------------+--------------------------+
|        info_name         |        info_value        |
+--------------------------+--------------------------+
| name                     | bulkload_data_index      |
| creator                  | system_admin             |
| created_time             | 2024-05-27 16:11:23.928  |
| last_modified_time       | 2024-05-27 16:11:23.928  |
| comment                  |                          |
| properties               | (("analyzer","unicode")) |
| index_type               | inverted                 |
| table_name               | bulkload_data            |
| table_column             | data                     |
| index_size_in_data_file  | 0                        |
| index_size_in_index_file | 0                        |
| total_index_size         | 0                        |
+--------------------------+--------------------------+

删除倒排索引

语法

DROP INDEX [IF EXISTS] index_name;

参数说明:

  • DROP INDEX:删除索引的关键字。
  • IF EXISTS:可选参数,如果指定的索引不存在,则不报错。
  • index_name:要删除的索引名称。

说明

执行drop index会立即成功,会删除掉index的元数据信息。但是不会立即删除索引存储信息

联系我们
预约咨询
微信咨询
电话咨询