Spanner 提供了一组内置统计信息表,可帮助您深入了解查询、读取和事务。为了将统计信息与您的 您可以添加一个标记(自由格式的 字符串),在您的 应用代码。这些标记将填充在统计信息表中,以帮助您根据标记进行关联和搜索。
Spanner 支持两种类型的标记:request 标记和 transaction 代码。顾名思义,您可以向事务添加事务标记,向各个查询和读取 API 添加请求标记。您可以在事务范围内设置事务标记,并可为事务中的每个适用 API 请求设置单个请求标记。在应用代码中设置的请求标记和事务标记将填充到以下统计信息表的列中。
统计信息表 | 统计信息表中填充的标记类型 |
---|---|
TopN 查询统计信息 | 请求标记 |
TopN 读取统计信息 | 请求标记 |
TopN 事务统计信息 | 事务标记 |
TopN 锁定统计信息 | 事务标记 |
请求标记
您可以向查询或读取请求添加可选的请求标记。Spanner
按请求标记对统计信息进行分组,该请求标记显示在REQUEST_TAG
和
查询统计信息
和
读取统计信息
表格。
何时使用请求标记
下面列举了可从使用请求标记获得好处的一些场景。
- 查找有问题的查询的来源或读取命令:Spanner 在内置统计信息表中收集读取和查询的统计信息。 如果您在统计信息表中发现速度缓慢的查询或 CPU 占用率高的读取,如果您已经为这些查询或读取分配了标记,就可以根据标记中的信息识别调用这些操作的来源(应用/微服务)。
- 识别统计信息表中的读取或查询:分配请求标记可帮助根据您感兴趣的标记过滤统计信息表中的行。
- 了解来自特定应用或微服务的查询是否速度缓慢:请求标记可帮助确定来自特定应用或微服务的查询是否具有较长的延迟时间。
- 对一组读取或查询的统计信息分组:您可以使用请求标记来跟踪、比较和报告一组类似读取或查询的性能。例如,如果多个查询访问具有相同访问模式的一个表/一组表,您可以考虑向所有这些查询添加相同标记以一起跟踪它们。
如何分配请求标记
以下示例展示了如何使用 Spanner 设置请求标记 客户端库。
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
如何查看统计信息表中的请求标记
下面的查询返回 10 分钟的时间间隔内的查询统计信息。
SELECT t.text,
t.request_tag,
t.execution_count,
t.avg_latency_seconds,
t.avg_rows,
t.avg_bytes
FROM SPANNER_SYS.QUERY_STATS_TOP_10MINUTE AS t
LIMIT 3;
我们以下面的数据为例,作为我们从查询中得到的结果。
文本 | request_tag | execution_count | avg_latency_seconds | avg_rows | avg_bytes |
---|---|---|---|---|---|
SELECT SingerId, AlbumId, AlbumTitle FROM Albums | app=concert,env=dev,action=select | 212 | 0.025 | 21 | 2365 |
从订单中选择 *; | app=catalogsearch,env=dev,action=list | 55 | 0.02 | 16 | 33.35 |
SELECT SingerId, FirstName, LastName FROM Singers; | [empty string] | 154 | 0.048 | 42 | 486.33 |
从这个结果表中,我们可以看到如果您为查询分配了 REQUEST_TAG
,则查询会填充到统计信息表中。如果没有分配请求标记,则会显示为空字符串。
对于标记的查询,统计信息按标记进行汇总(例如,请求标记 app=concert,env=dev,action=select
的平均延迟时间为 0.025 秒)。如果未分配标记,则按查询汇总统计信息(例如,第三行中的查询的平均延迟时间为 0.048 秒)。
事务标记
可以将可选事务标记添加到个别事务。Spanner 按事务标记对统计信息进行分组,该标记可在
TRANSACTION_TAG
个字段
交易统计信息
表格。
何时使用事务标记
以下是使用事务标记的一些场景。
- 查找有问题的事务的来源:Spanner 收集事务中读写事务的统计信息 统计信息表格中。当您在事务统计信息表中发现慢速事务时,如果您已为这些事务分配标记,则可以根据标记中的信息识别调用这些事务的来源(应用/微服务)。
- 识别统计信息表中的事务:分配事务标记可帮助根据您感兴趣的标记过滤事务统计信息表中的行。如果没有事务标记,发现统计信息表示哪些操作的过程可能很麻烦。例如,对于事务统计信息,您必须检查所涉及的表和列,以识别未标记的事务。
- 了解来自特定应用或微服务的事务是否缓慢:事务标记可帮助确定来自特定应用或微服务的事务延迟时间较长。
- 对一组事务的统计信息分组:您可以使用事务标记来跟踪、比较和报告一组类似事务的性能。
- 了解哪些事务正在访问锁定冲突中涉及的列:事务标记可帮助查明导致锁定统计信息表中的锁定冲突的各个事务。
- 使用变更数据流将用户变更数据从 Spanner 流式传输出去: 变更数据流数据记录包含相关事务的事务标记 修改用户数据的权限。这样一来,变更数据流的读取方 根据代码将更改与交易类型相关联。
如何分配事务标记
以下示例展示了如何使用 Spanner 设置事务标记 客户端库。使用客户端库时,您可以在事务调用的开头设置事务标记,该事务标记将应用于该事务中的所有单个操作。
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
如何查看“事务统计信息”表中的事务标记
下面的查询返回 10 分钟的时间间隔内的事务统计信息。
SELECT t.fprint,
t.transaction_tag,
t.read_columns,
t.commit_attempt_count,
t.avg_total_latency_seconds
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE AS t
LIMIT 3;
我们以下面的数据为例,作为我们从查询中得到的结果。
fprint | transaction_tag | read_columns | commit_attempt_count | avg_total_latency_seconds |
---|---|---|---|---|
40015598317 | app=concert,env=dev | [Venues._exists, Venues.VenueId, Venues.VenueName, Venues.Capacity] |
278802 | 0.3508 |
20524969030 | app=product,service=payment | [Singers.SingerInfo] | 129012 | 0.0142 |
77848338483 | [empty string] | [Singers.FirstName, Singers.LastName, Singers._exists] | 5357 | 0.048 |
从这个结果表中,我们可以看到如果您为事务分配了 TRANSACTION_TAG
,则会将它填充到事务统计信息表中。如果没有分配事务标记,则会显示为空字符串。
对于已标记的事务,统计信息将会按事务标记汇总(例如事务标记 app=concert,env=dev
的平均延迟时间为 0.3508 秒)。如果未分配标记,则统计信息将会按 FPRINT
汇总(例如,第三行中的 77848338483 的平均延迟时间为 0.048 秒)。
如何查看“锁定统计信息”表中的事务标记
下面的查询返回 10 分钟的时间间隔内的锁定统计信息。
CAST()
函数将 row_range_start_key
BYTES 字段转换为 STRING。
SELECT
CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
s.lock_wait_seconds,
s.sample_lock_requests
FROM SPANNER_SYS.LOCK_STATS_TOP_10MINUTE s
LIMIT 2;
我们以下面的数据为例,作为我们从查询中得到的结果。
row_range_start_key | lock_wait_seconds | sample_lock_requests |
---|---|---|
Songs(2,1,1) | 0.61 | LOCK_MODE: ReaderShared COLUMN: Singers.SingerInfo TRANSACTION_TAG: app=product,service=shipping LOCK_MODE: WriterShared COLUMN: Singers.SingerInfo TRANSACTION_TAG: app=product,service=payment |
albums(2,1+) | 0.48 | LOCK_MODE: ReaderShared COLUMN: users._exists1 TRANSACTION_TAG: [empty string] LOCK_MODE: WriterShared COLUMN: users._exists TRANSACTION_TAG: [empty string] |
从这个结果表中,我们可以看到如果您为事务分配了 TRANSACTION_TAG
,则会将它填充到锁定统计信息表中。如果没有分配事务标记,则会显示为空字符串。
API 方法与请求/事务标记之间的映射
请求标记和事务标记适用于特定的 API 方法,具体取决于事务模式是只读事务还是读写事务。一般而言,事务标记适用于读写事务,而请求标记适用于只读事务。下表显示了从 API 方法到适用标记类型的映射。
API 方法 | 事务模式 | 请求标记 | 事务标记 |
---|---|---|---|
Read, StreamingRead |
只读事务 | 是 | 否 |
读写事务 | 是 | 是 | |
ExecuteSql、 ExecuteStreamingSql1 |
只读事务1 | 有1 | 否 |
读写事务 | 是 | 是 | |
ExecuteBatchDml | 读写事务 | 是 | 是 |
BeginTransaction | 读写事务 | 否 | 是 |
提交 | 读写事务 | 否 | 是 |
1 适用于使用 Apache Beam SpannerIO 执行的变更数据流查询
Dataflow 连接器,REQUEST_TAG
包含一个 Dataflow 作业名称。
限制
向读取、查询和事务添加标记时,请考虑以下限制:
- 标记字符串的长度上限为 50 个字符。超出此限制的字符串会被截断。
- 标记中只允许使用 ASCII 字符 (32-126)。任意 Unicode 字符会被替换为下划线。
- 任何前导下划线 (_) 字符都会从字符串中移除。
- 标记区分大小写。例如,如果您将请求标记
将
APP=cart,ENV=dev
添加到一组查询,并将app=cart,env=dev
添加到 另一组查询,Spanner 会分别汇总统计信息 每个代码对应的 IP 地址 在以下情况下,统计信息表中可能缺少标记:
- 如果 Spanner 无法存储所有已标记的统计信息, 在表的时间间隔内运行的操作,系统会优先考虑 哪些操作在 。
标记命名
为数据库操作分配标记时,请务必考虑要在每个标记字符串中传达的信息。您选择的惯例或模式可使您的标记更有效。例如,正确的标记命名可以更轻松地将统计信息与应用代码相关联。
您可以在规定的限制中选择所需的任何标记。不过,我们建议您将标记字符串构建为一组以英文逗号分隔的键值对。
例如,假设您使用 Spanner 数据库
电子商务用例。您可能希望在要分配给特定查询的请求标记中包含有关应用、开发环境以及查询会执行的操作的信息。您可以将键值对格式的标记字符串考虑为 app=cart,env=dev,action=update
。这意味着查询会从开发环境中的购物车应用调用,并用于更新购物车。
假设您有一个来自目录搜索应用的查询,并将标记字符串指定为 app=catalogsearch,env=dev,action=list
。现在,如果这些查询中的任何一个在查询统计信息表中显示为高延迟查询,那么您可以通过使用标记轻松识别来源。
以下是一些示例,展示了可如何使用标记模式来整理操作统计信息。这些示例并不详尽;您还可以使用分隔符(如英文逗号)在标记字符串中组合它们。
标记键 | 标记/值对的示例 | 说明 |
---|---|---|
应用 | app=cart app=frontend app=catalogsearch |
帮助识别正在调用操作的应用。 |
环境 | env=prod env=dev env=test env=staging |
帮助识别与操作关联的环境。 |
框架 | framework=spring framework=django framework=jetty |
帮助识别与操作关联的框架。 |
操作 | action=list action=retrieve action=update |
帮助识别操作所执行的操作。 |
服务 | service=payment service=shipping |
帮助识别调用操作的微服务。 |
注意事项
- 当您分配
REQUEST_TAG
时,具有相同标记字符串的多个查询的统计信息会在查询统计信息表中分组为一行。TEXT
字段中仅显示这些查询之一的文本。 - 当您分配
REQUEST_TAG
时,具有相同标记字符串的多个读取的统计信息会在读取统计信息表中分组为一行。读取的所有列集都会添加到READ_COLUMNS
字段。 - 当您分配
TRANSACTION_TAG
时,具有相同标记字符串的事务的统计信息会在事务统计信息表中分组为一行。通过事务写入的所有列集都会添加到WRITE_CONSTRUCTIVE_COLUMNS
字段,读取的所有列集都会添加到READ_COLUMNS
字段。
使用标记进行问题排查的场景
查找有问题的事务的来源
以下查询返回所选时间段内热门事务的原始数据。
SELECT
fprint,
transaction_tag,
ROUND(avg_total_latency_seconds,4) as avg_total_latency_sec,
ROUND(avg_commit_latency_seconds,4) as avg_commit_latency_sec,
commit_attempt_count,
commit_abort_count
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
WHERE interval_end = "2020-05-17T18:40:00"
ORDER BY avg_total_latency_seconds DESC;
下表列出了我们的查询返回的示例数据,其中有三个应用(即购物车、产品和前端),它们拥有或者查询同一数据库。
识别出遇到高延迟的事务之后,您可以使用关联的标记来识别应用代码的相关部分,并使用事务统计信息进一步排查问题。
fprint | transaction_tag | avg_total_latency_sec | avg_commit_latency_sec | commit_attempt_count | commit_abort_count |
---|---|---|---|---|---|
7129109266372596045 | app=cart,service=order | 0.3508 | 0.0139 | 278802 | 142205 |
9353100217060788102 | app=cart,service=redis | 0.1633 | 0.0142 | 129012 | 27177 |
9353100217060788102 | app=product,service=payment | 0.1423 | 0.0133 | 5357 | 636 |
898069986622520747 | app=product,service=shipping | 0.0159 | 0.0118 | 4269 | 1 |
9521689070912159706 | app=frontend,service=ads | 0.0093 | 0.0045 | 164 | 0 |
11079878968512225881 | [empty string] | 0.031 | 0.015 | 14 | 0 |
同样,请求标记可用于从查询统计信息表中查找有问题的查询的来源,从读取统计信息表中查找有问题的读取的来源。
查找特定应用或微服务中事务的延迟时间和其他统计信息
如果您在标记字符串中使用了应用名称或微服务名称,则可帮助按包含该应用名称或微服务名称的标记过滤事务统计信息表。
假设您向付款应用添加了新事务,并想要查看这些新事务的延迟时间和其他统计信息。如果您在标记中使用了付款应用的名称,则可以过滤交易统计信息表以仅显示包含 app=payment
的那些标记。
以下查询返回 10 分钟的时间间隔内付款应用的事务统计信息。
SELECT
transaction_tag,
avg_total_latency_sec,
avg_commit_latency_sec,
commit_attempt_count,
commit_abort_count
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
WHERE STARTS_WITH(transaction_tag, "app=payment")
LIMIT 3;
以下是部分示例输出:
transaction_tag | avg_total_latency_sec | avg_commit_latency_sec | commit_attempt_count | commit_abort_count |
---|---|---|---|---|
app=payment,action=update | 0.3508 | 0.0139 | 278802 | 142205 |
app=payment,action=transfer | 0.1633 | 0.0142 | 129012 | 27177 |
app=payment, action=retrieve | 0.1423 | 0.0133 | 5357 | 636 |
同样,您可以使用请求标记在查询统计信息表或读取统计信息表中查找特定应用的查询或读取。
发现锁定冲突中涉及的事务
为了确定哪些事务和行键遭遇了较长的锁定等待时间,我们查询 LOCK_STAT_TOP_10MINUTE
表,其中列出了行锁定冲突所涉及的行键、列和相应的事务。
SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
t.total_lock_wait_seconds,
s.lock_wait_seconds,
s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
s.sample_lock_requests
FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
WHERE
t.interval_end = "2020-05-17T18:40:00" and s.interval_end = t.interval_end;
以下是我们的查询的部分输出示例:
row_range_start_key | total_lock_wait_seconds | lock_wait_seconds | frac_of_total | sample_lock_requests |
---|---|---|---|---|
Singers(32) | 2.37 | 1.76 | 1 | LOCK_MODE: WriterShared COLUMN: Singers.SingerInfo TRANSACTION_TAG: app=cart,service=order LOCK_MODE: ReaderShared COLUMN: Singers.SingerInfo TRANSACTION_TAG: app=cart,service=redis |
从这个结果表中,我们可以看到键为 SingerId=32 的 Singers
表中发生了冲突。Singers.SingerInfo
是 ReaderShared
和 WriterShared
之间发生锁定冲突的列。您还可以确定遇到冲突的相应事务(app=cart,service=order
和 app=cart,service=redis
)。
在查明导致锁冲突的事务之后,您现在可以使用事务统计信息关注这些事务,以便更好地了解这些事务正在执行的操作,您是否可以避免冲突或缩短锁定的持有时间。如需了解详情,请参阅减少锁争用的最佳做法。
后续步骤
- 了解其他内省工具。
- 有关 Spanner 为每个数据库存储的其他信息,请参阅 数据库的信息架构表。
- 详细了解 SQL 最佳实践 Spanner。
- 详细了解调查高 CPU 利用率。