目标
本教程将介绍如何使用基于 REST 的 Cloud Spanner API 完成以下步骤:
- 创建 Spanner 实例和数据库。
- 写入、读取数据库中的数据和对数据执行 SQL 查询。
- 更新数据库架构。
- 向数据库添加二级索引。
- 使用索引来读取数据和对数据执行 SQL 查询。
- 使用只读事务检索数据。
如果您想使用 Spanner 客户端库而不是 REST API,请参阅教程。
费用
本教程使用 Spanner,它是 Google Cloud如需了解 Spanner 的使用费用,请参阅 定价。
准备工作
- 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
进行 REST 调用的方式
您可以使用以下方式进行 Spanner REST 调用:
- Spanner API 参考文档中的 Try-It! 功能。本页面展示的示例使用的是 Try-It! 功能。
- Google APIs Explorer,其中包含 Cloud Spanner API 和其他 Google API。
- 其他支持 HTTP 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 项目。
- 点击
projects.instanceConfigs.list
。 对于 parent,输入:
projects/[PROJECT_ID]
点击 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
值。
创建实例
- 点击
projects.instances.create
。 对于 parent,输入:
projects/[PROJECT_ID]
点击 Add request body parameters 并选择
instance
。点击 instance 提示气泡以查看可能的字段。为以下字段添加值:
nodeCount
:输入1
。config
:输入在列出实例配置时返回的其中一个区域实例配置的name
值。displayName
:输入Test Instance
。
点击 instance 右括号后面的提示气泡,然后选择 instanceId。
对于
instanceId
,输入test-instance
。
您的 Try It! 实例创建页面现在应该如下所示:点击执行。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
您可以使用 projects.instances.list
列出您的实例。
创建数据库
创建一个名为 example-db
的数据库。
- 点击
projects.instances.databases.create
。 对于 parent,输入:
projects/[PROJECT_ID]/instances/test-instance
点击 Add request body parameters 并选择
createStatement
。对于
createStatement
,输入:CREATE DATABASE `example-db`
(数据库名称
example-db
包含一个连字符,所以必须用反引号 (`
) 括起来)。点击 Execute。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
您可以使用 projects.instances.databases.list
列出您的数据库。
创建架构
使用 Spanner 的数据定义语言 (DDL) 来创建、修改或删除表,以及创建或删除索引。
- 点击“
projects.instances.databases.updateDdl
”。 对于 database,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
对于 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 语句。点击 Execute。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
该架构为一个基本的音乐应用定义了两个表:Singers
和 Albums
。本页面会一直用到这两个表。请查看示例架构(如果您还没有查看)。
您可以使用 projects.instances.databases.getDdl
检索您的架构。
创建会话
在添加、更新、删除或查询数据之前,您必须先创建 session:代表与 Spanner 数据库服务。(如果您使用的是 Spanner 客户端库,则不需要直接使用会话,因为客户端库会代表您管理会话。)
- 点击“
projects.instances.databases.sessions.create
”。 对于 database,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
点击 Execute。
响应会显示您创建的会话,形式为
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
在读取或写入数据库时,您将使用此会话。
会话设计为长期有效。Spanner 数据库服务
删除会话。尝试使用已删除的会话会导致 NOT_FOUND
错误。如果遇到此错误,请创建并使用新会话。您可以使用 projects.instances.databases.sessions.get
查看会话是否仍处于活动状态。如需了解相关信息,请参阅使空闲会话保持活动状态。
下一步是将数据写入数据库。
写入数据
您可以使用 Mutation
类型来写入数据。Mutation
是用于变更操作的容器。Mutation
表示插入、更新、删除等一系列操作,
以原子方式应用于 Spanner 中的不同行和表
数据库。
- 点击“
projects.instances.databases.sessions.commit
”。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 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" ] ] } } ] }
点击 Execute。该响应会显示提交时间戳。
本示例使用了 insertOrUpdate
。Mutations
的其他操作包括 insert
、update
、replace
和 delete
。
如需了解如何对数据类型进行编码,请参阅 TypeCode。
使用 SQL 查询数据
- 点击
projects.instances.databases.sessions.executeSql
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" }
点击 Execute。该响应会显示查询结果。
使用读取 API 读取数据
- 点击
projects.instances.databases.sessions.read
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "table": "Albums", "columns": [ "SingerId", "AlbumId", "AlbumTitle" ], "keySet": { "all": true } }
点击 Execute。该响应会显示读取结果。
更新数据库架构
假设您需要将名为 MarketingBudget
的新列添加到 Albums
表,此操作需要更新您的数据库架构。Spanner 支持在数据库继续处理流量的同时,对数据库进行架构更新。架构更新不需要使数据库离线,并且不会锁定整个表或列;在架构更新期间,您可以继续将数据写入数据库。
添加列
- 点击
projects.instances.databases.updateDdl
。 对于 database,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
对于 Request body,使用以下内容:
{ "statements": [ "ALTER TABLE Albums ADD COLUMN MarketingBudget INT64" ] }
statements
数组包含用于定义架构的 DDL 语句。点击执行。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
将数据写入新列
以下代码可将数据写入新列。对于键分别为 Albums(1, 1)
和 Albums(2, 2)
的行,该代码会将 MarketingBudget
分别设置为 100000
和 500000
。
- 点击
projects.instances.databases.sessions.commit
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "singleUseTransaction": { "readWrite": {} }, "mutations": [ { "update": { "table": "Albums", "columns": [ "SingerId", "AlbumId", "MarketingBudget" ], "values": [ [ "1", "1", "100000" ], [ "2", "2", "500000" ] ] } } ] }
点击 Execute。该响应会显示提交时间戳。
您还可以执行 SQL 查询或读取调用来获取刚才写入的值。
以下是执行查询的方法:
- 点击
projects.instances.databases.sessions.executeSql
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "sql": "SELECT SingerId, AlbumId, MarketingBudget FROM Albums" }
点击 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
添加索引。
- 点击
projects.instances.databases.updateDdl
。 对于 database,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
对于 Request body,使用以下内容:
{ "statements": [ "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)" ] }
点击 Execute。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
使用索引进行查询
- 点击
projects.instances.databases.sessions.executeSql
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "sql": "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums WHERE AlbumTitle >= 'Aardvark' AND AlbumTitle < 'Goo'" }
点击 Execute。作为响应的一部分,您应该可以看到以下几行:
"rows": [ [ "2", "Go, Go, Go", null ], [ "2", "Forever Hold Your Peace", "500000" ] ]
使用索引进行读取
- 点击
projects.instances.databases.sessions.read
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "table": "Albums", "columns": [ "AlbumId", "AlbumTitle" ], "keySet": { "all": true }, "index": "AlbumsByAlbumTitle" }
点击 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 索引。
- 点击
projects.instances.databases.updateDdl
。 对于 database,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
对于 Request body,使用以下内容:
{ "statements": [ "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)" ] }
点击 Execute。即使在 REST 调用返回响应之后,该操作也可能需要几分钟才能完成。该响应会返回一个长时间运行的操作,您可以查询该操作以检查其状态。
现在,当您执行读取操作时便可从 AlbumsByAlbumTitle2
索引中提取所有 AlbumId
、AlbumTitle
和 MarketingBudget
列:
- 点击
projects.instances.databases.sessions.read
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "table": "Albums", "columns": [ "AlbumId", "AlbumTitle", "MarketingBudget" ], "keySet": { "all": true }, "index": "AlbumsByAlbumTitle2" }
点击 Execute。作为响应的一部分,您应该可以看到以下几行:
"rows": [ [ "2", "Forever Hold Your Peace", "500000" ], [ "2", "Go, Go, Go", null ], [ "1", "Green", null ], [ "3", "Terrified", null ], [ "1", "Total Junk", "100000" ] ]
使用只读事务检索数据
假设您要在同一时间戳执行多个读取操作。只读事务会观察事务提交记录的一致前缀,以便您的应用始终获得一致的数据。
创建只读事务
- 点击
projects.instances.databases.sessions.beginTransaction
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
对于 Request Body,使用以下内容:
{ "options": { "readOnly": {} } }
点击 Execute。
该响应会显示您创建的事务的 ID。
现在您可以使用只读事务以一致的时间戳检索数据,即使自创建只读事务后数据已更改。
使用只读事务运行查询
- 点击
projects.instances.databases.sessions.executeSql
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums", "transaction": { "id": "[TRANSACTION_ID]" } }
点击 Execute。您应该会在响应中看到与以下内容类似的行:
"rows": [ [ "2", "2", "Forever Hold Your Peace" ], [ "1", "2", "Go, Go, Go" ], [ "2", "1", "Green" ], [ "2", "3", "Terrified" ], [ "1", "1", "Total Junk" ] ]
使用只读事务进行读取
- 点击
projects.instances.databases.sessions.read
。 对于 session,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db/sessions/[SESSION]
(您在创建会话时会收到此值。)
对于 Request body,使用以下内容:
{ "table": "Albums", "columns": [ "SingerId", "AlbumId", "AlbumTitle" ], "keySet": { "all": true }, "transaction": { "id": "[TRANSACTION_ID]" } }
点击 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 账号产生额外费用,请删除数据库和您创建的实例。
删除数据库
- 点击
projects.instances.databases.dropDatabase
。 对于 name,输入:
projects/[PROJECT_ID]/instances/test-instance/databases/example-db
点击 Execute。
删除实例
- 点击
projects.instances.delete
。 对于 name,输入:
projects/[PROJECT_ID]/instances/test-instance
点击 Execute。
后续步骤
- 在虚拟机实例中访问 Spanner:创建一个 有权访问 Spanner 数据库的虚拟机实例。
- 详细了解 Spanner 概念。