在 Spanner 数据库中,Spanner 会自动为每个表的主键创建索引。例如,您无需执行任何操作即可为 Singers 的主键编制索引,因为系统会自动为您编制索引。
除此之外,您可以针对非主键列创建“二级索引”。针对非主键列添加二级索引有助于更高效地查找此类列中的数据。例如,如果您需要按标题快速查找专辑,则应对 AlbumTitle 创建二级索引,这样 Spanner 便无需扫描整个表。
如果上述示例中的查找在读写事务中完成,则更高效的查找还可以避免对整个表保持锁定,从而允许针对 AlbumTitle 查找范围以外的行对表进行并发插入和更新。
除了为查询带来的优势之外,二级索引还可以帮助 Spanner 更高效地执行扫描,从而启用索引扫描而不是全表扫描。
Spanner 会将以下数据存储在各个二级索引中:
- 基表中的所有键列
- 索引中包含的所有列
- 索引定义的可选
STORING子句(GoogleSQL 方言数据库)或INCLUDE子句(PostgreSQL 方言数据库)中指定的所有列。
随着时间的推移,Spanner 会逐步分析您的表,以确保为适合的查询使用二级索引。
添加二级索引
在创建表时添加二级索引最为高效。要同时创建表及其索引,请在一个请求中将添加新表和添加新索引的 DDL 语句一并发送到 Spanner。
在 Spanner 中,您还可以向现有表添加新的二级索引,添加过程中数据库可继续处理流量。与 Spanner 中的其他所有架构更改一样,向现有数据库添加索引不需要使数据库离线,也不会锁定整个列或表。
每当新索引添加到现有表时,Spanner 就会自动回填或填充索引,以反映被编入索引的数据的最新视图。Spanner 会为您管理此回填过程,该过程会以低优先级使用节点资源在后台运行。索引回填速度会适应在创建索引期间不断变化的节点资源,并且回填不会显著影响数据库的性能。
创建索引可能需要几分钟到数小时才能完成。由于创建索引属于架构更新,因此须遵循与其他架构更新一样的性能限制。创建二级索引所需的时间取决于以下几个因素:
- 数据集的大小
- 实例的计算容量
- 实例上的负载
如需查看索引回填流程的进度,请参阅进度部分。
请注意,将提交时间戳列用作二级索引的第一部分可能会引发热点,并降低写入性能。
使用 CREATE INDEX 语句在您的架构中定义二级索引。下面是一些示例:
要按名字和姓氏将数据库中的所有 Singers 编入索引,请使用以下语句:
GoogleSQL
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
PostgreSQL
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
要按照 SongName 的值为数据库中的所有 Songs 创建索引,请使用以下语句:
GoogleSQL
CREATE INDEX SongsBySongName ON Songs(SongName);
PostgreSQL
CREATE INDEX SongsBySongName ON Songs(SongName);
要仅将特定歌手的歌曲编入索引,请使用 INTERLEAVE IN 子句在表 Singers 中交错索引:
GoogleSQL
CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName),
INTERLEAVE IN Singers;
PostgreSQL
CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName)
INTERLEAVE IN Singers;
要仅将特定专辑中的歌曲编入索引,请使用以下语句:
GoogleSQL
CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName),
INTERLEAVE IN Albums;
PostgreSQL
CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName)
INTERLEAVE IN Albums;
要按照 SongName 的降序顺序编制索引,请使用以下语句:
GoogleSQL
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC),
INTERLEAVE IN Albums;
PostgreSQL
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC)
INTERLEAVE IN Albums;
请注意,上面的 DESC 注解仅适用于 SongName。要按其他索引键的降序顺序编制索引,请同样为这些索引键添加注释 DESC,例如:SingerId DESC, AlbumId DESC。
另请注意,PRIMARY_KEY 是保留字,不能用作索引的名称。它是伪索引的名称,是在创建具有 PRIMARY KEY 规范的表时创建的
有关选择非交错索引和交错索引的详细信息和最佳做法,请参阅索引选项和在其值单调递增或递减的列上使用交错索引。
索引和交织
Spanner 索引可以与其他表交织,以便将索引行与其他表中的行放置在一起。与 Spanner 表交织类似,索引父项的主键列必须是索引列的前缀,并且在类型和排序顺序上相匹配。与交织表不同,列名称匹配不是必需的。交织索引的每一行都会与关联的父行一起存储。
例如,请考虑以下架构:
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo PROTO<Singer>(MAX)
) PRIMARY KEY (SingerId);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
PublisherId INT64 NOT NULL
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
PublisherId INT64 NOT NULL,
SongName STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE TABLE Publishers (
Id INT64 NOT NULL,
PublisherName STRING(MAX)
) PRIMARY KEY (Id);
如需按名字和姓氏将数据库中的所有 Singers 编入索引,您必须创建索引。以下演示了如何定义索引 SingersByFirstLastName:
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
如果您想对 (SingerId, AlbumId, SongName) 创建 Songs 索引,可以执行以下操作:
CREATE INDEX SongsBySingerAlbumSongName
ON Songs(SingerId, AlbumId, SongName);
或者,您可以创建一个与 Songs 的祖先交织的索引,如下所示:
CREATE INDEX SongsBySingerAlbumSongName
ON Songs(SingerId, AlbumId, SongName),
INTERLEAVE IN Albums;
此外,您还可以在 (PublisherId, SingerId, AlbumId, SongName) 上创建 Songs 索引,该索引与非 Songs 祖先的表(如 Publishers)交织。请注意,Publishers 表的主键 (id) 不是以下示例中编入索引的列的前缀。这种情况仍然是允许的,因为 Publishers.Id 和 Songs.PublisherId 具有相同的类型、排序顺序以及可为 null 性。
CREATE INDEX SongsByPublisherSingerAlbumSongName
ON Songs(PublisherId, SingerId, AlbumId, SongName),
INTERLEAVE IN Publishers;
检查索引回填进度
控制台
在 Spanner 导航菜单中,点击操作标签页。操作页面会显示正在运行的操作的列表。
在列表中找到回填操作。如果任务仍在运行,结束时间列中的进度指示器会显示操作已完成的百分比,如下图所示:

gcloud
使用 gcloud spanner operations describe 检查操作的进度。
获取操作 ID:
gcloud spanner operations list --instance=INSTANCE-NAME \ --database=DATABASE-NAME --type=DATABASE_UPDATE_DDL
替换以下内容:
- 将 INSTANCE-NAME 替换为 Spanner 实例名称。
- 将 DATABASE-NAME 替换为数据库的名称。
使用说明:
要限制列表,请指定
--filter标志。例如:--filter="metadata.name:example-db"仅列出对特定数据库的操作。--filter="error:*"仅列出失败的备份操作。
如需了解过滤条件语法,请参阅 gcloud topic filters。如需了解如何过滤备份操作,请参阅 ListBackupOperationsRequest 中的
filter字段。--type标志不区分大小写。
输出类似于以下内容:
OPERATION_ID STATEMENTS DONE @TYPE _auto_op_123456