本页面介绍了如何添加搜索索引。系统会对搜索索引中的条目运行全文搜索。
如何使用搜索索引
您可以针对要用于全文搜索的任何列创建搜索索引。要创建搜索索引,请使用
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。TOKENLIST
在 AlbumTitle
(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
语句,使用 AlbumTitle
和 Rating
的令牌创建搜索索引:
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 位整数。
搜索索引在内部实现为两级映射:
- Docid 的令牌
- 文档 ID 与基表主键
与 Spanner 相比,此方案可节省大量存储空间
因此无需为每个对象存储完整的基表主键
<token, document>
对。
有两种类型的物理索引,可以实现两个级别的 映射:
- 将分区键和 Docid 映射到基表的二级索引
主键。在上一部分中的示例中,这会将
{SingerId, ReleaseTimestamp, uid}
映射到{AlbumId}
。辅助索引还会存储CREATE SEARCH INDEX
的STORING
子句中指定的所有列。 - 令牌索引,用于将令牌映射到文档 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 列。column1 和 column2 值的组合在分区中必须是唯一的。 |
使用说明:
- 内部 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 时 对表进行拆分 分布搜索索引数据,以便特定基表行中的所有令牌 在同一个分块中换句话说,搜索索引是按文档分片的。这种分片策略对性能有重大影响:
- 与每项事务通信的服务器数量
常量,而不考虑词元数量或编入索引的
TOKENLIST
列。 - 涉及多个条件表达式的搜索查询会在每个分块上独立执行,从而避免与分布式联接相关的性能开销。
搜索索引有两种分布模式:
- 统一分片(默认)。在均匀分片中,每个基表行的编入索引数据会随机分配到分区的索引分块。
- 排序顺序分片。在排序分片中,每个基表行的相关数据会根据
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
或将其留空时,系统会使用均匀分片创建搜索索引。
交错搜索索引
与二级索引一样,您可以在以下位置交错搜索索引: 基表的父表。使用交错搜索的主要原因 索引是将基表数据与小型分区的索引数据共置的。 这种机会性主机托管具有以下优势:
- 写入操作无需执行两阶段提交。
- 搜索索引与基表的后联接不是分布式。
交错搜索索引具有以下限制:
- 仅排序顺序分片 索引 它们可以交错
- 搜索索引只能在顶级表(而不是子表)中交错 表)。
- 与交错表和二级索引一样,将父表的键设为交错搜索索引中
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;
搜索索引排序顺序必须符合以下要求:
- 仅使用
INT64
列进行排序 搜索索引的顺序。大小任意的列会在搜索索引中使用过多资源,因为 Spanner 需要在每个令牌旁边存储一个 docid。具体来说,排序 排序列不能使用TIMESTAMP
类型,因为TIMESTAMP
使用 纳秒精度,不适合 64 位整数。 排序列不得为
NULL
。您可以通过以下两种方式满足此要求:- 将排序顺序列声明为
NOT NULL
。 - 将索引配置为排除 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 过滤,以根据任何任意条件排除行。如需了解详情,请参阅使用生成的列创建部分索引。
后续步骤
- 了解标记化和 Spanner 标记生成器。
- 了解数字索引。
- 了解索引分区。