Spanner 使用入门(通过 REST)


目标

本教程将介绍如何使用基于 REST 的 Cloud Spanner API 完成以下步骤:

  • 创建 Spanner 实例和数据库。
  • 写入、读取数据库中的数据和对数据执行 SQL 查询。
  • 更新数据库架构。
  • 向数据库添加二级索引。
  • 使用索引来读取数据和对数据执行 SQL 查询。
  • 使用只读事务检索数据。

如果您想使用 Spanner 客户端库,而不是 REST API,请参阅教程

费用

本教程使用 Spanner,它是 Google Cloud 的收费组件。如需了解 Spanner 的使用费用,请参阅价格

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  5. 确保您的 Google Cloud 项目已启用结算功能

进行 REST 调用的方式

您可以使用以下方式进行 Spanner REST 调用:

本页面使用的约定

  • 这些示例使用 [PROJECT_ID] 作为 Google Cloud 项目 ID。请将 [PROJECT_ID] 替换为您的 Google Cloud 项目 ID。(请勿在项目 ID 中包含 []。)

  • 这些示例创建并使用 test-instance 的实例 ID。如果您未使用 test-instance,请替换您的实例 ID。

  • 这些示例创建并使用 example-db 的数据库 ID。如果您未使用 example-db,请替换您的数据库 ID。

  • 这些示例使用 [SESSION] 作为会话名称的一部分。请将 [SESSION] 替换成您在创建会话时收到的值。(请勿在会话名称中包含 []。)

  • 这些示例使用 [TRANSACTION_ID] 的事务 ID。请将 [TRANSACTION_ID] 替换成您在创建事务时收到的值。(请勿在事务 ID 中包含 []。)

  • Try-It! 功能支持以交互方式添加个别 HTTP 请求字段。本主题中的大多数示例都提供了整个请求,而不是描述如何以交互方式将个别字段添加到请求中。

实例

首次使用 Spanner 时,必须创建一个实例,用于分配 Spanner 数据库所使用的资源。创建实例时,您可以选择数据的存储位置以及实例具有的计算容量

列出实例配置

在创建实例时,您可以指定一个实例配置,用于定义该实例中数据库的地理位置和复制。您可以选择单区域配置以将数据存储在单个区域中,也可以选择多区域配置以将数据分布到多个区域。如需了解详情,请参阅实例

使用 projects.instanceConfigs.list 确定哪些配置可用于您的 Google Cloud 项目。

  1. 点击 projects.instanceConfigs.list
  2. 对于 parent,输入:

    projects/[PROJECT_ID]
    
  3. 点击 Execute。响应中会显示可用的实例配置。以下是一个示例响应(您的项目可能会采用不同的实例配置):

    {
      "instanceConfigs": [
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-asia-south1",
          "displayName": "asia-south1"
        },
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-asia-east1",
          "displayName": "asia-east1"
        },
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-asia-northeast1",
          "displayName": "asia-northeast1"
        },
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-europe-west1",
          "displayName": "europe-west1"
        },
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-us-east4",
          "displayName": "us-east4"
        },
        {
          "name": "projects/[PROJECT_ID]/instanceConfigs/regional-us-central1",
          "displayName": "us-central1"
        }
      ]
    }
    

创建实例时,您可以使用其中一个实例配置的 name 值。

创建实例

  1. 点击 projects.instances.create
  2. 对于 parent,输入:

    projects/[PROJECT_ID]
    
  3. 点击 Add request body parameters 并选择 instance

  4. 点击 instance 提示气泡以查看可能的字段。为以下字段添加值:

    1. nodeCount:输入 1
    2. config:输入在列出实例配置时返回的其中一个区域实例配置的 name 值。
    3. displayName:输入 Test Instance
  5. 点击 instance 右括号后面的提示气泡,然后选择 instanceId

  6. 对于 instanceId,输入 test-instance
    您的 Try It! 实例创建页面现在应该如下所示:

    实例创建界面屏幕截图

  7. 点击执行。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

您可以使用 projects.instances.list 列出您的实例。

创建数据库

创建一个名为 example-db 的数据库。

  1. 点击 projects.instances.databases.create
  2. 对于 parent,输入:

    projects/[PROJECT_ID]/instances/test-instance
    
  3. 点击 Add request body parameters 并选择 createStatement

  4. 对于 createStatement,输入:

    CREATE DATABASE `example-db`
    

    (数据库名称 example-db 包含一个连字符,所以必须用反引号 (`) 括起来)。

  5. 点击 Execute。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

您可以使用 projects.instances.databases.list 列出您的数据库。

创建架构

使用 Spanner 的数据定义语言 (DDL) 创建、修改或删除表,以及创建或删除索引。

  1. 点击“projects.instances.databases.updateDdl”。
  2. 对于 database,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 对于 Request body,使用以下内容:

    {
      "statements": [
        "CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)",
        "CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX)) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE"
      ]
    }
    

    statements 数组包含用于定义架构的 DDL 语句。

  4. 点击 Execute。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

该架构为一个基本的音乐应用定义了两个表:SingersAlbums。本页面会一直用到这两个表。请查看示例架构(如果您还没有查看)。

您可以使用 projects.instances.databases.getDdl 检索您的架构。

创建会话

在添加、更新、删除或查询数据之前,您必须创建一个会话,该会话代表与 Spanner 数据库服务的通信渠道。(如果您使用的是 Spanner 客户端库,则无需直接使用会话,因为客户端库会代表您管理会话。)

  1. 点击“projects.instances.databases.sessions.create”。
  2. 对于 database,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 点击 Execute

  4. 响应会显示您创建的会话,形式为

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    在读取或写入数据库时,您将使用此会话。

会话设计为长期有效。Spanner 数据库服务可以在会话空闲超过一小时时删除会话。尝试使用已删除的会话会导致 NOT_FOUND 错误。如果遇到此错误,请创建并使用新会话。您可以使用 projects.instances.databases.sessions.get 查看会话是否仍处于活动状态。如需了解相关信息,请参阅使空闲会话保持活动状态

下一步是将数据写入数据库。

写入数据

您可以使用 Mutation 类型来写入数据。Mutation 是用于变更操作的容器。Mutation 表示插入、更新、删除和其他操作序列,这些操作可以原子方式应用于 Spanner 数据库中的不同行和表。

  1. 点击“projects.instances.databases.sessions.commit”。
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "singleUseTransaction": {
        "readWrite": {}
      },
      "mutations": [
        {
          "insertOrUpdate": {
            "table": "Singers",
            "columns": [
              "SingerId",
              "FirstName",
              "LastName"
            ],
            "values": [
              [
                "1",
                "Marc",
                "Richards"
              ],
              [
                "2",
                "Catalina",
                "Smith"
              ],
              [
                "3",
                "Alice",
                "Trentor"
              ],
              [
                "4",
                "Lea",
                "Martin"
              ],
              [
                "5",
                "David",
                "Lomond"
              ]
            ]
          }
        },
        {
          "insertOrUpdate": {
            "table": "Albums",
            "columns": [
              "SingerId",
              "AlbumId",
              "AlbumTitle"
            ],
            "values": [
              [
                "1",
                "1",
                "Total Junk"
              ],
              [
                "1",
                "2",
                "Go, Go, Go"
              ],
              [
                "2",
                "1",
                "Green"
              ],
              [
                "2",
                "2",
                "Forever Hold Your Peace"
              ],
              [
                "2",
                "3",
                "Terrified"
              ]
            ]
          }
        }
      ]
    }
    
  4. 点击 Execute。该响应会显示提交时间戳。

本示例使用了 insertOrUpdateMutations 的其他operations包括 insertupdatereplacedelete

如需了解如何对数据类型进行编码,请参阅 TypeCode

使用 SQL 查询数据

  1. 点击 projects.instances.databases.sessions.executeSql
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums"
    }
    
  4. 点击 Execute。该响应会显示查询结果。

使用读取 API 读取数据

  1. 点击 projects.instances.databases.sessions.read
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "table": "Albums",
      "columns": [
        "SingerId",
        "AlbumId",
        "AlbumTitle"
      ],
      "keySet": {
        "all": true
      }
    }
    
  4. 点击 Execute。该响应会显示读取结果。

更新数据库架构

假设您需要将名为 MarketingBudget 的新列添加到 Albums 表,此操作需要更新您的数据库架构。Spanner 支持在数据库继续处理流量的同时对数据库进行架构更新。架构更新不需要使数据库离线,并且不会锁定整个表或列;在架构更新期间,您可以继续将数据写入数据库。

添加列

  1. 点击 projects.instances.databases.updateDdl
  2. 对于 database,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 对于 Request body,使用以下内容:

    {
      "statements": [
        "ALTER TABLE Albums ADD COLUMN MarketingBudget INT64"
      ]
    }
    

    statements 数组包含用于定义架构的 DDL 语句。

  4. 点击执行。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

将数据写入新列

以下代码可将数据写入新列。对于键分别为 Albums(1, 1)Albums(2, 2) 的行,该代码会将 MarketingBudget 分别设置为 100000500000

  1. 点击 projects.instances.databases.sessions.commit
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "singleUseTransaction": {
        "readWrite": {}
      },
      "mutations": [
        {
          "update": {
            "table": "Albums",
            "columns": [
              "SingerId",
              "AlbumId",
              "MarketingBudget"
            ],
            "values": [
              [
                "1",
                "1",
                "100000"
              ],
              [
                "2",
                "2",
                "500000"
              ]
            ]
          }
        }
      ]
    }
    
  4. 点击 Execute。该响应会显示提交时间戳。

您还可以执行 SQL 查询或读取调用来获取刚才写入的值。

以下是执行查询的方法:

  1. 点击 projects.instances.databases.sessions.executeSql
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "sql": "SELECT SingerId, AlbumId, MarketingBudget FROM Albums"
    }
    
  4. 点击 Execute。作为响应的一部分,您应该可以看到包含更新后的 MarketingBudget 值的两行:

    "rows": [
      [
        "1",
        "1",
        "100000"
      ],
      [
        "1",
        "2",
        null
      ],
      [
        "2",
        "1",
        null
      ],
      [
        "2",
        "2",
        "500000"
      ],
      [
        "2",
        "3",
        null
      ]
    ]
    

使用二级索引

假设您想要提取 Albums 表中 AlbumTitle 值在特定范围内的所有行。您可以使用 SQL 语句或读取调用读取 AlbumTitle 列中的所有值,然后舍弃不符合条件的行。不过,执行全表扫描费用高昂,特别是对内含大量行的表来说更是如此。相反,如果对表创建二级索引,按非主键列进行搜索,则可以提高行检索速度。

向现有表添加二级索引需要更新架构。与其他架构更新一样,Spanner 支持在数据库继续处理流量的同时添加索引。Spanner 会自动使用您的现有数据回填索引。回填可能需要几分钟时间才能完成,但在此过程中,您无需使数据库离线,也无需避免写入特定的表或列。如需了解详情,请参阅索引回填

添加二级索引后,Spanner 会自动将其用于 SQL 查询,如果使用该索引后运行速度可能会更快。如果使用读取接口,则必须指定要使用的索引。

添加二级索引

您可以使用 updateDdl 添加索引。

  1. 点击 projects.instances.databases.updateDdl
  2. 对于 database,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 对于 Request body,使用以下内容:

    {
      "statements": [
        "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"
      ]
    }
    
  4. 点击 Execute。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

使用索引进行查询

  1. 点击 projects.instances.databases.sessions.executeSql
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "sql": "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums WHERE AlbumTitle >= 'Aardvark' AND AlbumTitle < 'Goo'"
    }
    
  4. 点击 Execute。作为响应的一部分,您应该可以看到以下几行:

    "rows": [
      [
        "2",
        "Go, Go, Go",
        null
      ],
      [
        "2",
        "Forever Hold Your Peace",
        "500000"
      ]
    ]
    

使用索引进行读取

  1. 点击 projects.instances.databases.sessions.read
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "table": "Albums",
      "columns": [
        "AlbumId",
        "AlbumTitle"
      ],
      "keySet": {
        "all": true
      },
      "index": "AlbumsByAlbumTitle"
    }
    
  4. 点击 Execute。作为响应的一部分,您应该可以看到以下几行:

    "rows": [
      [
        "2",
        "Forever Hold Your Peace"
      ],
      [
        "2",
        "Go, Go, Go"
      ],
      [
        "1",
        "Green"
      ],
      [
        "3",
        "Terrified"
      ],
      [
        "1",
        "Total Junk"
      ]
    ]
    

添加带 STORING 子句的索引

您可能已经注意到,上面的读取示例不包括读取 MarketingBudget 列。这是因为 Spanner 的读取接口不支持将索引与数据表联接以查找未存储在索引中的值。

请创建 AlbumsByAlbumTitle 的备用定义,用于将 MarketingBudget 的副本存储到索引中。

您可以使用 updateDdl 添加 STORING 索引。

  1. 点击 projects.instances.databases.updateDdl
  2. 对于 database,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 对于 Request body,使用以下内容:

    {
      "statements": [
        "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)"
      ]
    }
    
  4. 点击 Execute。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。

现在,当您执行读取操作时便可从 AlbumsByAlbumTitle2 索引中提取所有 AlbumIdAlbumTitleMarketingBudget 列:

  1. 点击 projects.instances.databases.sessions.read
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "table": "Albums",
      "columns": [
        "AlbumId",
        "AlbumTitle",
        "MarketingBudget"
      ],
      "keySet": {
        "all": true
      },
      "index": "AlbumsByAlbumTitle2"
    }
    
  4. 点击 Execute。作为响应的一部分,您应该可以看到以下几行:

    "rows": [
      [
        "2",
        "Forever Hold Your Peace",
        "500000"
      ],
      [
        "2",
        "Go, Go, Go",
        null
      ],
      [
        "1",
        "Green",
        null
      ],
      [
        "3",
        "Terrified",
        null
      ],
      [
        "1",
        "Total Junk",
        "100000"
      ]
    ]
    

使用只读事务检索数据

假设您要在同一时间戳执行多个读取操作。只读事务会观察事务提交记录的一致前缀,以便您的应用始终获得一致的数据。

创建只读事务

  1. 点击 projects.instances.databases.sessions.beginTransaction
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    
  3. 对于 Request Body,使用以下内容:

    {
      "options": {
        "readOnly": {}
      }
    }
    
  4. 点击 Execute

  5. 该响应会显示您创建的事务的 ID。

现在您可以使用只读事务以一致的时间戳检索数据,即使自创建只读事务后数据已更改。

使用只读事务运行查询

  1. 点击 projects.instances.databases.sessions.executeSql
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums",
      "transaction": {
        "id": "[TRANSACTION_ID]"
      }
    }
    
  4. 点击 Execute。您应该会在响应中看到与以下内容类似的行:

    "rows": [
      [
        "2",
        "2",
        "Forever Hold Your Peace"
      ],
      [
        "1",
        "2",
        "Go, Go, Go"
      ],
      [
        "2",
        "1",
        "Green"
      ],
      [
        "2",
        "3",
        "Terrified"
      ],
      [
        "1",
        "1",
        "Total Junk"
      ]
    ]
    

使用只读事务进行读取

  1. 点击 projects.instances.databases.sessions.read
  2. 对于 session,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
    

    (您在创建会话时会收到此值。)

  3. 对于 Request body,使用以下内容:

    {
      "table": "Albums",
      "columns": [
        "SingerId",
        "AlbumId",
        "AlbumTitle"
      ],
      "keySet": {
        "all": true
      },
      "transaction": {
        "id": "[TRANSACTION_ID]"
      }
    }
    
  4. 点击 Execute。您应该会在响应中看到与以下内容类似的行:

    "rows": [
      [
        "1",
        "1",
        "Total Junk"
      ],
      [
        "1",
        "2",
        "Go, Go, Go"
      ],
      [
        "2",
        "1",
        "Green"
      ],
      [
        "2",
        "2",
        "Forever Hold Your Peace"
      ],
      [
        "2",
        "3",
        "Terrified"
      ]
    ]
    

Spanner 还支持读写事务,即在单个逻辑时间点以原子方式执行一组读写。如需了解详情,请参阅读写事务。(Try-It! 功能不适合演示读写事务。)

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生额外费用,请删除数据库和您创建的实例。

删除数据库

  1. 点击 projects.instances.databases.dropDatabase
  2. 对于 name,输入:

    projects/[PROJECT_ID]/instances/test-instance/databases/example-db
    
  3. 点击 Execute

删除实例

  1. 点击 projects.instances.delete
  2. 对于 name,输入:

    projects/[PROJECT_ID]/instances/test-instance
    
  3. 点击 Execute

后续步骤