会话概览
本页面介绍了 Spanner 中会话的高级概念,包括创建客户端库、使用 REST 或 RPC API 或使用 Google 客户端库时会话的最佳做法。
会话表示与 Spanner 数据库服务的通信通道。会话用于执行在 Spanner 数据库中读取、写入或修改数据的事务。每个会话只应用于单个数据库。
会话一次只能执行一个事务。独立读取、写入和查询在内部使用事务,并计入一个事务限制。
会话缓存的性能优势
创建会话的开销很大。为了避免每次进行数据库操作时影响性能,客户端应该保留“会话缓存”,这是准备就绪可供使用的可用会话池。缓存应存储现有会话,并响应请求返回适当类型的会话,以及清理未使用的会话。如需查看有关如何实现会话缓存的示例,请参阅其中一个 Spanner 客户端库(例如 Go 客户端库或 Java 客户端库)的源代码。
会话设计为长期有效,因此在某会话用于数据库操作之后,客户端应将该会话返回到缓存,以供重复使用。
使用 Google 客户端库时的最佳做法
下面介绍了在 Spanner 中使用 Google 客户端库的最佳做法。
配置会话数
一般来说,我们不建议修改客户端库使用的默认会话数。
如果您有一个特殊的工作负载,我们建议将下限设置为预期的并发事务数,并将上限设置为初始测试数,例如 100。如果上限不够,请提高上限。增加活跃会话的数量会在 Spanner 数据库服务上使用额外的资源,因此不清理未使用的会话可能会降低性能。我们还建议每个 gRPC 通道的会话数不超过 100。
管理写入会话比例
对于大多数客户端库,Spanner 会为读写事务保留一部分会话,称为“写入会话比例”。如果您的应用用尽所有读取会话,则 Spanner 会使用读写会话,甚至适用于只读事务。读写会话需要 spanner.databases.beginOrRollbackReadWriteTransaction
权限。如果用户具有 spanner.databaseReader IAM 角色,则调用失败,并且 Spanner 会返回以下错误消息:
generic::permission_denied: Resource %resource% is missing IAM permission:
spanner.databases.beginOrRollbackReadWriteTransaction
对于维持写入会话比例的客户端库,您可以对其进行设置。
C++
所有 C ++ 会话都相同。没有只读或只读写会话。
C#
C# 的默认写入会话比例是 0.2。您可以使用 SessionPoolOptions 的 WriteSessionsFraction 字段更改比例。
Go
Go 的默认写入会话比例为 0.2。您可以使用 SessionPoolConfig 的 WriteSessions 字段更改比例。
Java
所有 Java 会话都相同。没有只读或只读写会话。
Node.js
Node.js 的默认写入会话比例是 0(零)。您可以使用 writes 字段来更改比例。
PHP
所有 PHP 会话都是相同的。没有只读或只读写会话。
Python
Python 支持四种不同的会话池类型,可用于管理读取和读写会话。
Ruby
Ruby 的默认写入会话比例是 0.3。您可以使用 client 初始化方法更改比例。
创建客户端库或使用 REST/RPC 时的最佳做法
下面介绍了在 Spanner 的客户端库中实现会话或者通过 REST 或 RPC API 使用会话的最佳做法。
这些最佳做法仅在您开发客户端库或使用 REST/RPC API 的情况下适用。如果您使用的是 Cloud Spanner 的 Google Cloud 客户端库之一,请参阅使用 Google 客户端库时的最佳做法。
创建会话缓存并确定其大小
要确定某客户端进程的会话缓存的最佳大小,请将下限设置为预期的并发事务数,并将上限设置为初始测试数(例如 100)如果上限不够,请提高上限。增加活跃会话的数量会在 Spanner 数据库服务上使用额外的资源,因此不清理未使用的会话可能会降低性能。对于使用 RPC API 的用户,我们建议每个 gRPC 通道的会话数不超过 100。
处理已删除的会话
可以通过以下三种方式删除会话:
- 客户端可以删除会话。
- Spanner 数据库服务可以在会话处于空闲状态超过 1 小时时删除会话。
- 如果某个会话的存在时间超过 28 天,Spanner 数据库服务可能会删除该会话。
尝试使用已删除的会话会导致 NOT_FOUND
错误。如果您遇到此错误,请创建和使用新会话,将新会话添加到缓存中,并从缓存中移除已删除的会话。
使空闲会话保持活跃状态
Spanner 数据库服务保留丢弃未使用的会话的权利。如果您确实需要让空闲会话保持活跃状态,例如,在预计数据库使用近期内会显著增长的情况下,您可以防止会话被删除。执行开销较小的操作(例如,执行 SQL 查询 SELECT 1
)可使会话保持活跃状态。如果您有近期不需要的空闲会话,可以让 Spanner 丢弃该会话,然后在下次需要会话时创建新会话。
让会话保持活动状态的一种情况是应对数据库上的常规高峰需求。如果每天的上午 9:00 到下午 6:00 大量使用数据库,则应在这段时间内保留一些空闲会话,因为高峰使用期间可能需要这些会话。下午 6:00 以后,您可以让 Spanner 丢弃空闲会话。在每天上午 9:00 之前,请创建一些新会话,以便它们能够满足预期需求。
另一种情况是,如果您的应用使用 Spanner,但必须在使用时避免连接开销,您可以将一组会话保持活动状态以避免连接开销。
向客户端库用户隐藏会话详细信息
如果您正在创建客户端库,请勿将会话曝露给客户端库使用者。让客户端能够进行数据库调用,但无需涉及会话创建和维护的复杂性。如需查看对客户端库使用方隐藏会话详细信息的客户端库示例,请参阅适用于 Java 的 Spanner 客户端库。
处理非幂等写入事务的错误
没有重试保护的写入事务可能会多次应用变更。
如果某一变更不具有幂等性,则多次应用变更可能会导致失败。例如,即使在写入尝试之前某行不存在,插入操作也可能会失败并出现 ALREADY_EXISTS
错误。如果后端服务器提交了变更但无法将成功信息传达给客户端,则可能发生这种情况。在这种情况下,可能会重试变更,导致 ALREADY_EXISTS
失败。
在实现您自己的客户端库或使用 REST API 时,可以采用以下方法来应对这种情况:
- 设计您的写入代码结构,使其具有幂等性。
- 对写入使用重试保护。
- 实现一个执行“upsert”逻辑的方法:若是新的则插入,若已存在则更新。
- 代替客户端处理错误。
保持稳定连接
为获得最佳性能,用于承载会话的连接应保持稳定。当托管会话的连接发生更改时,Spanner 可能会中止会话中的活跃事务,并在更新会话元数据时对数据库产生少量额外负载。少数连接偶尔发生变化是无关紧要的,但应避免同时更改大量连接的情况。如果您在客户端和 Spanner 之间使用代理,则应保持每个会话的连接稳定性。
监控活跃会话
您可以从命令行、通过 REST API 或 RPC API 使用 ListSessions 命令,监控数据库中的活跃会话。ListSessions 可显示给定数据库的活跃会话。如果您需要查明会话泄漏的原因,这一命令非常有用。(会话泄漏是指系统创建会话,但未将其返回到会话缓存以重复使用的突发事件。)
借助 ListSessions,您能够查看有关活跃会话的元数据,包括创建会话的时间以及上次使用会话的时间。在排查会话问题时,分析此数据可以为您指明正确的方向。如果大多数活跃会话没有最近的 approximate_last_use_time
,这可能表明会话没有被您的应用合理地重复使用。如需详细了解 approximate_last_use_time
字段,请参阅 RPC API 参考文档。
如需详细了解如何使用 ListSessions,请参阅 REST API 参考文档、RPC API 参考文档或 gcloud 命令行工具参考文档。