二级索引

在 Spanner 数据库中,Spanner 会自动为每个表的主键创建索引。例如,您无需执行任何操作即可为 Singers 的主键编制索引,因为系统会自动为您编制索引。

除此之外,您可以针对非主键列创建“二级索引”。针对非主键列添加二级索引有助于更高效地查找此类列中的数据。例如,如果您需要按标题快速查找专辑,则应对 AlbumTitle 创建二级索引,这样 Spanner 便无需扫描整个表。

如果上述示例中的查找在读写事务中完成,则更高效的查找还可以避免对整个表保持锁定,从而允许针对 AlbumTitle 查找范围以外的行对表进行并发插入和更新。

除了为查询带来的优势之外,二级索引还可以帮助 Spanner 更高效地执行扫描,从而启用索引扫描而不是全表扫描。

Spanner 会将以下数据存储在各个二级索引中:

随着时间的推移,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;

检查索引回填进度

控制台

  1. 在 Spanner 导航菜单中,点击操作标签页。操作页面会显示正在运行的操作的列表。

  2. 在列表中找到回填操作。如果任务仍在运行,结束时间列中的进度指示器会显示操作已完成的百分比,如下图所示:

    显示 98% 的进度指示器的屏幕截图

gcloud

使用 gcloud spanner operations describe 检查操作的进度。

  1. 获取操作 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