搜索索引

本页面介绍了如何添加搜索索引。系统会对搜索索引中的条目运行全文搜索

如何使用搜索索引

您可以针对要用于全文搜索的任何列创建搜索索引。要创建搜索索引,请使用 CREATE SEARCH INDEX DDL 语句。更新索引 使用 ALTER SEARCH INDEX DDL 语句。Spanner 会自动构建和维护搜索索引,包括在数据库中发生变化时立即在搜索索引中添加和更新数据。

搜索索引分区

搜索索引可以是分区非分区,具体取决于您要加速的查询类型。

  • 分区索引是最佳选择的一个示例是, 应用查询电子邮件邮箱。每个查询都被限制 邮箱。

  • 例如,如果要对商品目录中的所有商品类别进行查询,则最好使用非分区查询。

搜索索引用例

除全文搜索外,Spanner 搜索索引还支持 以下:

  • 子字符串搜索,是一种在较大文本正文中搜索较短字符串(子字符串)的查询。
  • 将任何索引数据子集的条件组合到单个索引扫描中。

搜索索引支持将非文本数据(例如数字和 完全匹配字符串,那么搜索索引最常见的用例是将 文本。

搜索索引示例

为了显示搜索索引的功能,我们假设有一个表, 存储有关音乐专辑的信息:

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);

Spanner 提供了几个标记化函数, 词元。要修改上表,以便用户运行全文搜索来查找 使用 TOKENIZE_FULLTEXT 函数从影集标题中创建词元 专辑标题。然后,创建一个使用 TOKENLIST 数据类型的列,用于存储 TOKENIZE_FULLTEXT 中的令牌化输出。在本示例中 我们创建 AlbumTitle_Tokens 列。

ALTER TABLE Albums
  ADD COLUMN AlbumTitle_Tokens TOKENLIST
  AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;

以下示例使用 CREATE SEARCH INDEX DDL 创建搜索索引 (AlbumsIndex) 对 AlbumTitle 令牌 (AlbumTitle_Tokens):

CREATE SEARCH INDEX AlbumsIndex
  ON Albums(AlbumTitle_Tokens);

添加搜索索引后,使用 SQL 查询查找匹配的影集 搜索条件。例如:

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")

数据一致性

创建索引时,Spanner 会使用自动化流程 回填数据以确保一致性。当提交写入时,索引 会在同一事务中进行更新Spanner 自动 执行数据一致性检查。

搜索索引架构定义

搜索索引是在表的一个或多个 TOKENLIST 列上定义的。搜索索引包含以下组件:

  • 基表:需要编入索引的 Spanner 表。
  • TOKENLIST:一组列,用于定义需要编入索引的令牌。这些列的顺序并不重要。

例如,在以下语句中,基表为 Albums。TOKENLISTAlbumTitle (AlbumTitle_Tokens) 和 Rating 中创建列 (Rating_Tokens)。

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  SingerId INT64 NOT NULL,
  ReleaseTimestamp INT64 NOT NULL,
  AlbumTitle STRING(MAX),
  Rating FLOAT64,
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
  Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN
) PRIMARY KEY(AlbumId);

使用以下 CREATE SEARCH INDEX 语句,使用 AlbumTitleRating 的令牌创建搜索索引:

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC

搜索索引具有以下选项:

  • 分区:用于划分搜索索引的可选列组。查询分区索引通常比查询非分区索引更高效。如需了解详情,请参阅分区搜索索引
  • 排序顺序列:一个可选的 INT64 列,用于确定从搜索索引检索的顺序。如需了解详情,请参阅搜索索引排序顺序
  • 交错:与二级索引一样,您可以交错搜索索引。交错搜索索引使用更少的资源来写入和联接 基表。如需了解详情,请参阅交错搜索 索引
  • Options 子句:用于替换搜索索引的默认设置的键值对列表。

如需了解详情,请参阅 CREATE SEARCH INDEX 参考文档。

搜索索引的内部布局

搜索索引内部表示法的一个重要元素是 docid,它可作为基准表的主键(可以任意长度)的高效存储表示法。它还会根据 CREATE SEARCH INDEX 子句中用户提供的 ORDER BY 列创建内部数据布局的顺序。它表示为一个或两个 64 位整数。

搜索索引在内部实现为两级映射:

  1. Docid 的令牌
  2. 文档 ID 与基表主键

与 Spanner 相比,此方案可节省大量存储空间 因此无需为每个对象存储完整的基表主键 <token, document> 对。

有两种类型的物理索引,可以实现两个级别的 映射:

  1. 将分区键和 Docid 映射到基表的二级索引 主键。在上一部分中的示例中,这会将 {SingerId, ReleaseTimestamp, uid} 映射到 {AlbumId}。辅助索引还会存储 CREATE SEARCH INDEXSTORING 子句中指定的所有列。
  2. 令牌索引,用于将令牌映射到文档 ID,类似于信息检索文献中的倒排索引。Spanner 会为搜索索引的每个 TOKENLIST 维护一个单独的令牌索引。从逻辑上来说 词元索引维护每个分区内每个词元的 Docid 列表 (在信息检索中称为“帖子列表”)。这些列表按顺序排列 来快速检索,而在列表中,docid 用于排序。 各个词元索引是一个实现细节,不会通过 Spanner API。

Spanner 支持以下四种 docid 选项。

搜索索引 Docid 行为
搜索索引省略了 ORDER BY 子句 {uid} Spanner 会添加一个隐藏的唯一值 (UID) 来标识每行。
ORDER BY column {column, uid} Spanner 会将 UID 列添加为分区中具有相同 column 值的行之间的裁定程序。
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) {column} 系统不会添加 UID 列。column 值在分区内必须是唯一的。
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) {column1, column2} 未添加 UID 列。column1column2 值的组合在分区中必须是唯一的。

使用说明:

  • 内部 UID 列不通过 Spanner 公开 API。
  • 在未添加 UID 的索引中,添加包含 已存在(分区、排序顺序)失败。

例如,请考虑以下数据:

AlbumId SingerId ReleaseTimestamp SongTitle
a1 1 997 美好的一天
a2 1 743 美丽的眼睛

假设预排序列按升序排列,则按 SingerId 分区的令牌索引的内容会按如下方式分区:

SingerId _token ReleaseTimestamp uid
1 美丽 743 uid1
1 美丽 997 uid2
1 743 uid1
1 眼睛 997 uid2

搜索索引分片

当 Spanner 时 对表进行拆分 分布搜索索引数据,以便特定基表行中的所有令牌 在同一个分块中换句话说,搜索索引是按文档分片的。这种分片策略对性能有重大影响:

  1. 与每项事务通信的服务器数量 常量,而不考虑词元数量或编入索引的 TOKENLIST 列。
  2. 涉及多个条件表达式的搜索查询会在每个分块上独立执行,从而避免与分布式联接相关的性能开销。

搜索索引有两种分布模式:

  • 统一分片(默认)。在均匀分片中,每个基表行的编入索引数据会随机分配到分区的索引分块。
  • 排序顺序分片。在排序分片中,每个基表行的相关数据会根据 ORDER BY 列分配到分区的索引分块。例如,在降序排序的情况下,排序值最高的所有行都会显示在分区的第一个索引分块中,排序值次高的一组排序值会显示在下一个分块中。

这些分片模式在热点风险和查询开销之间存在权衡:

  • 排序顺序分片搜索索引在索引 已按时间戳排序有关详情,请参阅选择主键以 阻止 热点。 另一方面,如果一系列文档的写入负载增加, 均匀分片可确保增加量在 处理。
  • 基于负载的标准拆分会创建额外的分块, 并充分防范出现热点问题。均匀分片的缺点是,它可能会为某些类型的查询使用更多资源。

搜索索引的分片模式使用 OPTIONS 子句进行配置:

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);

设置 sort_order_sharding=false 或将其留空时,系统会使用均匀分片创建搜索索引。

交错搜索索引

与二级索引一样,您可以在以下位置交错搜索索引: 基表的父表。使用交错搜索的主要原因 索引是将基表数据与小型分区的索引数据共置的。 这种机会性主机托管具有以下优势:

  • 写入操作无需执行两阶段提交
  • 搜索索引与基表的后联接不是分布式。

交错搜索索引具有以下限制:

  1. 排序顺序分片 索引 它们可以交错
  2. 搜索索引只能在顶级表(而不是子表)中交错 表)。
  3. 与交错表和二级索引一样,将父表的键设为交错搜索索引中 PARTITION BY 列的前缀。

定义交错搜索索引

以下示例演示了如何定义交错搜索索引:

CREATE TABLE Singers (
  SingerId INT64 NOT NULL
) PRIMARY KEY(SingerId);

CREATE TABLE Albums (
  SingerId INT64 NOT NULL,
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
) PRIMARY KEY(SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
PARTITION BY SingerId,
INTERLEAVE IN Singers
OPTIONS (sort_order_sharding = true);

搜索索引排序顺序

搜索索引排序规则定义的要求与二级索引的要求不同。

例如,请参考下表:

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  ReleaseTimestamp INT64 NOT NULL,
  AlbumName STRING(MAX),
  AlbumName_Token TOKENLIST AS (TOKEN(AlbumName)) HIDDEN
) PRIMARY KEY(AlbumId);

应用可以定义一个二级索引,以便使用按 ReleaseTimestamp 排序的 AlbumName 查找信息:

CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);

等效的搜索索引如下所示(由于二级索引不支持全文搜索,因此此索引使用完全匹配的令牌化):

CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;

搜索索引排序顺序必须符合以下要求:

  1. 仅使用 INT64 列进行排序 搜索索引的顺序。大小任意的列会在搜索索引中使用过多资源,因为 Spanner 需要在每个令牌旁边存储一个 docid。具体来说,排序 排序列不能使用TIMESTAMP类型,因为TIMESTAMP使用 纳秒精度,不适合 64 位整数。
  2. 排序列不得为 NULL。您可以通过以下两种方式满足此要求:

    1. 将排序顺序列声明为 NOT NULL
    2. 将索引配置为排除 NULL 值

时间戳通常用于确定排序顺序。常见的做法是 使用自 Unix 纪元以来的微秒数

应用通常会先使用按降序排序的搜索索引检索最新数据。

过滤后的 NULL 搜索索引

搜索索引可以使用 WHERE column IS NOT NULL 语法 排除基表中的行。NULL 过滤可应用于分区键、排序列和存储列。不对存储的数组列执行 NULL 过滤 允许。

示例

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING Genre
WHERE Genre IS NOT NULL

查询必须指定 NULL 过滤条件(Genre IS NOT NULL )。WHERE否则,查询优化器将无法使用搜索索引。如需了解详情,请参阅 SQL 查询要求

对生成的列使用 NULL 过滤,以根据任何任意条件排除行。如需了解详情,请参阅使用生成的列创建部分索引

后续步骤