查询计划和时间轴

BigQuery 的诊断查询计划和耗时信息嵌入在查询作业中。这类似于其他数据库和分析系统中由 EXPLAIN 等语句提供的信息。这些信息可从 jobs.get 等方法的 API 响应中检索到。

对于长时间运行的查询,BigQuery 将定期更新这些统计信息。这些更新的发生与轮询作业状态的速率无关,但频率通常不会超过每 30 秒一次。此外,未利用执行资源的查询作业(例如试运行请求,或可从缓存结果提供的结果)将不包含额外的诊断信息,但可能存在其他统计信息。

背景

BigQuery 在执行查询作业时,会将声明式 SQL 语句转换为执行图表,而后分解为一系列查询阶段,而这些阶段本身由更细化的多组执行步骤构成。BigQuery 利用高度分布式的并行架构来运行这些查询,而阶段则模拟很多潜在工作器可能并行执行的工作单元。各阶段通过快速分布式 Shuffle 架构相互通信,相关内容已在其他地方更详细地讨论过。

在查询计划中,计划专门用于传达有关并行性的信息,因此会用到工作单元和工作器这两个术语。在 BigQuery 的其他位置,您可能会遇到“槽”这个术语;该术语是查询执行的多个方面(包括计算、内存和 I/O 资源)的抽象表示。借助这种抽象计量方式,顶级作业统计信息可通过查询的 totalSlotMs 估算值提供单次查询的大致费用信息。

查询执行架构的另一个重要属性是它具有动态性,这意味着查询计划可在查询处于运行状态时修改。在查询运行时引入的阶段通常用于改进查询工作器中的数据分布。在发生这种情况的查询计划中,这些通常会被标记为“重新分区”阶段。

除了查询计划之外,查询作业还会显示执行时间轴,该时间轴用于提供查询工作器内处于已完成、待处理和活跃状态的工作单元的计量信息。一个查询可能会同时经历多个具有活跃工作器的阶段,因此时间轴用于显示查询的整体进度。

使用经典版 BigQuery 网页界面查看信息

如果您使用的是经典版 BigQuery 网页界面,则可以通过点击 Details 按钮(位于 Results 按钮右侧)查看已完成查询的查询计划详细信息。

查询计划的屏幕截图

对于长时间运行的查询,您可以通过点击查询编辑器窗格下方查询状态行中的链接,查看查询计划的进度。

所用时间状态行的屏幕截图

查询计划信息

在 API 响应中,查询计划表示为一系列查询阶段,其中显示了每个阶段的概览统计信息、详细的步骤信息和阶段耗时分类。并非所有详细信息都显示在网页界面中,不过它们会显示在 API 响应中。

阶段概览

每个阶段的概览字段可能包括以下各项:

API 字段 说明
id 阶段的唯一数字 ID。
name 阶段的简单概括名称。阶段内的 steps 提供有关执行步骤的更多详细信息。
status 阶段的执行状态。可能的状态包括:待处理、正在运行、已完成、已失败和已取消。
inputStages 构成阶段的依赖关系图的一系列 ID。例如,JOIN 阶段通常需要两个依赖阶段,用于准备 JOIN 关系左右侧的数据。
startMs 时间戳(以纪元毫秒为单位),表示阶段中第一个工作器开始执行的时间。
endMs 时间戳(以纪元毫秒为单位),表示最后一个工作器结束执行的时间。
steps 阶段中更加详细的执行步骤列表。要了解详情,请参阅下一部分。
recordsRead 所有阶段工作器的阶段输入大小(即记录数)。
recordsWritten 所有阶段工作器的阶段输出大小(即记录数)。
parallelInputs 阶段的可并行工作单元数。这可能表示表中的数字列式细分,也可能表示中间 Shuffle 的分区数,具体取决于阶段和查询。
completedParallelInputs 阶段中已完成的工作单元数量。对于某些查询,要完成某个阶段,并不一定需要完成该阶段中的每一个输入。
shuffleOutputBytes 表示查询阶段中写入所有工作器的总字节数。
shuffleOutputBytesSpilled 在阶段之间传输重要数据的查询可能需要回退到基于磁盘的传输。溢出的字节统计信息传达了溢出到磁盘的数据量。

每个阶段的步骤信息

步骤表示阶段中每个工作器必须执行的细化操作,展现为一个有序操作列表。步骤经过分类,其中一些操作提供更详细的信息。查询计划中显示的操作类别包括以下各项:

步骤 说明
READ 从输入表或中间 Shuffle 读取一列或多列。
WRITE 将一列或多列写入输出表或中间结果。对于阶段的 HASH 分区输出,这还包括用作分区键的列。
COMPUTE 诸如表达式评估和 SQL 函数之类的操作。
FILTER 执行 WHERE、OMIT IF 和 HAVING 子句的运算符。
SORT Sort 或 Order-By 操作,包括列键和方向。
AGGREGATE 聚合操作,例如 GROUP BY 或 COUNT。
LIMIT 执行 LIMIT 子句的运算符。
JOIN JOIN 操作,其中包括所用的连接类型和列。
ANALYTIC_FUNCTION 调用解析函数(也称为“窗口函数”)。
USER_DEFINED_FUNCTION 调用用户定义的函数。

每个阶段的耗时分类

查询阶段还以相对和绝对形式提供阶段耗时分类。由于每个执行阶段都表示由一个或多个独立工作器所执行的工作,因此会以平均耗时和最长耗时两种形式提供信息,分别代表一个阶段中所有工作器的平均性能以及指定分类下的最慢工作器性能。平均耗时和最大耗时被进一步分为绝对表示法和相对表示法。对于基于比率的统计信息,会以任何细分中任何工作器的最长耗时所占比例形式提供数据。

经典版 BigQuery 网页界面使用相对耗时表示法来显示阶段耗时。

阶段耗时信息报告如下:

相对耗时 绝对耗时 经典版 BigQuery 网页界面 * 比率分子 **
waitRatioAvg waitMsAvg waitRatioAvg 深黄色条 一般工作器等待安排的时间。
waitRatioMax waitMsMax waitRatioMax 黄色条 最慢的工作器等待安排的时间。
readRatioAvg readMsAvg readRatioAvg 深紫色条 一般工作器用于读取输入数据的时间。
readRatioMax readMsMax readRatioMax 紫色条 最慢的工作器用于读取输入数据的时间。
computeRatioAvg computeMsAvg computeRatioAvg 深橙色条 一般工作器受到 CPU 限制的时间。
computeRatioMax computeMsMax computeRatioMax 橙色条 最慢的工作器受 CPU 限制的时间。
writeRatioAvg writeMsAvg writeRatioAvg 深蓝色条 一般工作器用于写入输出数据的时间。
writeRatioMax writeMsMax writeRatioMax 蓝色条 最慢的工作器用于写入输出数据的时间。

*“AVG”和“MAX”这两个标签仅供演示之用,不会显示在经典版 BigQuery 网页界面中。

时间轴元数据

查询时间轴用于在特定时间点报告进度,同时提供总体查询进度的快照视图。时间轴由一系列示例表示,其中报告了以下详细信息:

字段 说明
elapsedMs 自查询执行以来经过的毫秒数。
totalSlotMs 查询使用的槽毫秒数的累计值。
pendingUnits 已计划和等待执行的工作单元总数。
activeUnits 工作器当前正在处理的活跃工作单元总数。
completedUnits 在执行此查询时已完成的工作单元总数。

查询示例

如果您运行一个简单查询来计算 Shakespeare 公开数据集内的行数,然后再运行第二个条件计数,将结果限制为引用 hamlet 的行数:

#StandardSQL
SELECT
  COUNT(1) as rowcount,
  COUNTIF(corpus = 'hamlet') as rowcount_hamlet
FROM `publicdata.samples.shakespeare`

您可以点击详细信息,以查看有关查询计划的以下信息。首先,我们来分析第一个部分,其中包含查询时间轴:

hamlet 查询时间轴的屏幕截图

在这个示例中,我们将处理一个非常小的示例表和简单的查询,所以总共只有两个工作单元。在此示例中,所有工作几乎会立即完成。

再往下,让我们看看查询计划:

hamlet 查询计划的屏幕截图

在这个示例中,颜色指示器显示了所有阶段的相对耗时。从并行输入信息中,我们看到每个阶段只需要一个工作器,因此平均耗时和最长耗时并无不同。

我们还可以看到,在这个极其简单的查询中,任何细分中的最长耗时即为阶段 01 中的单个工作器等待阶段 00 完成的时间。这是因为阶段 01 依赖于阶段 00 的输入,仅当第一个阶段将其输出(1 行,约 18 个字节)写入中间 Shuffle 后才能启动阶段 01。

现在,让我们来详细分析执行阶段的步骤。点击阶段标签左侧的三角形,以展开该阶段的详细信息:

hamlet 查询计划步骤详细信息的屏幕截图

在本例中,我们可以看到已完成阶段 00 工作的单个工作器的执行计划。首先,从引用的 shakespeare 表的“corpus”列中 READ(读取)数据。然后,为 COUNTCOUNTIF 预测建立 AGGREGATION(聚合)。扫描数据时需要执行 COMPUTE(计算)步骤,该步骤会同时为常规计数和条件计数提供数据,并将输出写入中间 Shuffle 输出(在此计划中标记为 __stage00_output)中。

错误报告

查询作业在执行过程中可能会失败。由于计划信息会定期更新,您可以查看在执行图表中的哪个位置发生故障。在界面中,成功和失败的阶段会通过阶段名称旁边的对勾和感叹号图标进行标示。

有关解读和处理错误的更多信息,请参阅问题排查指南

API 表示示例

查询计划信息将自动嵌入到作业响应信息中,无需调用其他方法,只需调用 jobs.get 即可检索作业详细信息。例如,以下是返回 hamlet 查询示例的作业的 JSON 响应摘录,其中显示了查询计划和时间轴信息。

"statistics": {
  "query": {
    "cacheHit": false,
    "queryPlan": [
      {
        "completedParallelInputs": "1",
        "computeMsAvg": "25",
        "computeMsMax": "25",
        "computeRatioAvg": 0.17857142857142858,
        "computeRatioMax": 0.17857142857142858,
        "endMs": "1522787349945",
        "id": "0",
        "name": "S00: Input",
        "parallelInputs": "1",
        "readMsAvg": "28",
        "readMsMax": "28",
        "readRatioAvg": 0.2,
        "readRatioMax": 0.2,
        "recordsRead": "164656",
        "recordsWritten": "1",
        "shuffleOutputBytes": "18",
        "shuffleOutputBytesSpilled": "0",
        "startMs": "1522787349898",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$1:corpus",
              "FROM publicdata.samples.shakespeare"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$20 := COUNT($30)",
              "$21 := COUNTIF($31)"
            ]
          },
          {
            "kind": "COMPUTE",
            "substeps": [
              "$30 := 1",
              "$31 := equal($1, 'hamlet')"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$20, $21",
              "TO __stage00_output"
            ]
          }
        ],
        "waitMsAvg": "0",
        "waitMsMax": "0",
        "waitRatioAvg": 0.0,
        "waitRatioMax": 0.0,
        "writeMsAvg": "5",
        "writeMsMax": "5",
        "writeRatioAvg": 0.03571428571428571,
        "writeRatioMax": 0.03571428571428571
      },
      {
        "completedParallelInputs": "1",
        "computeMsAvg": "14",
        "computeMsMax": "14",
        "computeRatioAvg": 0.1,
        "computeRatioMax": 0.1,
        "endMs": "1522787350180",
        "id": "1",
        "inputStages": [
          "0"
        ],
        "name": "S01: Output",
        "parallelInputs": "1",
        "readMsAvg": "0",
        "readMsMax": "0",
        "readRatioAvg": 0.0,
        "readRatioMax": 0.0,
        "recordsRead": "1",
        "recordsWritten": "1",
        "shuffleOutputBytes": "16",
        "shuffleOutputBytesSpilled": "0",
        "startMs": "1522787350038",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$20, $21",
              "FROM __stage00_output"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$10 := SUM_OF_COUNTS($20)",
              "$11 := SUM_OF_COUNTS($21)"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$10, $11",
              "TO __stage01_output"
            ]
          }
        ],
        "waitMsAvg": "140",
        "waitMsMax": "140",
        "waitRatioAvg": 1.0,
        "waitRatioMax": 1.0,
        "writeMsAvg": "129",
        "writeMsMax": "129",
        "writeRatioAvg": 0.9214285714285714,
        "writeRatioMax": 0.9214285714285714
      }
    ],
    "referencedTables": [
      {
        "datasetId": "samples",
        "projectId": "publicdata",
        "tableId": "shakespeare"
      }
    ],
    "statementType": "SELECT",
    "timeline": [
      {
        "activeUnits": "0",
        "completedUnits": "2",
        "elapsedMs": "999",
        "pendingUnits": "0",
        "totalSlotMs": "185"
      },
      {
        "activeUnits": "0",
        "completedUnits": "2",
        "elapsedMs": "1197",
        "pendingUnits": "0",
        "totalSlotMs": "185"
      }
    ],
    "totalBytesBilled": "10485760",
    "totalBytesProcessed": "2464625",
    "totalPartitionsProcessed": "0",
    "totalSlotMs": "127"
  },
  "totalBytesProcessed": "2464625"
},

利用执行情况信息

BigQuery 查询计划提供有关服务如何执行查询的信息,但服务的托管特性限制了某些详细信息是否可直接操作。很多优化只需使用该服务即可自动执行,这可能不同于其他需要由知识丰富的专门人员来完成调整、配置和监控的环境。

要了解可以完善查询执行和性能的特定做法,请参阅最佳做法文档。查询计划和时间轴统计信息可以帮助您了解某些阶段是否在资源耗用上占据大头。例如,生成的输出行远超输入行的 JOIN 阶段可能表示有必要在查询的早期阶段进行过滤。

此外,时间轴信息可以帮助确定某个查询速度缓慢是因为其自身原因,还是受到了其他查询争用相同资源的影响。如果您发现活跃单元的数量在查询的整个生命周期中始终有限,但排队的工作单元数量一直很高,这可能意味着减少并发查询数量将显著缩短某些查询的总体执行时间。

此页内容是否有用?请给出您的反馈和评价:

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面