# GraphQL API 的速率限制和查询限制

GitHub GraphQL API 设有限制，以防止向 GitHub 的服务器发出过多或滥用的请求。

## 主要速率限制

GraphQL API 为每个查询分配了点数，并且限制了可在特定时间内使用的点数。 此限制有助于防止滥用和拒绝服务攻击，并确保 API 仍可供所有用户使用。

REST API 还具有单独的主要速率限制。 有关详细信息，请参阅“[REST API 的速率限制](/zh/rest/using-the-rest-api/rate-limits-for-the-rest-api)”。

通常，可以根据身份验证方法计算 GraphQL API 的主要速率限制：

* *对于用户*：每位用户每小时 5,000 点。 这包括使用personal access token发出的请求，以及由GitHub App或OAuth app代表已授权该应用的用户发出的请求。 由 GitHub App 组织拥有的 GitHub Enterprise Cloud 代表用户发出的请求，具有更高的速率限制：每小时 10,000 点。 同样，如果你是 OAuth app 组织的成员，则由 GitHub Enterprise Cloud 组织拥有或批准的 GitHub Enterprise Cloud 代表你发出的请求，其速率限制为每小时 10,000 点数。
* *对于GitHub App不属于GitHub Enterprise Cloud组织* 的安装：每个安装每小时 5,000 点。 如果安装的仓库数量超过 20 个，则每仓库每小时另外 50 点。 如果某组织的安装拥有超过 20 位用户，则每位用户每小时另加 50 点。 速率限制不能超过每小时 12,500 点。 用户访问令牌（而不是安装访问令牌）的速率限制由用户的主要速率限制决定。
* *对于在GitHub AppGitHub Enterprise Cloud组织上的安装*：每个安装每小时 10,000 点。 用户访问令牌（而不是安装访问令牌）的速率限制由用户的主要速率限制决定。
* *对于 OAuth apps*：每小时 5,000 点；如果该应用由 GitHub Enterprise Cloud 组织拥有，则为每小时 10,000 点。 这仅适用于应用使用其客户端 ID 和客户端密码来请求公开数据时。 由 OAuth app 生成的 OAuth 访问令牌的速率限制由用户的主要速率限制决定。
* *对于`GITHUB_TOKEN`工作流中GitHub Actions*，每个存储库每小时 1,000 点。 对于 GitHub.com 上的企业帐户所属资源的请求，限制为每仓库每小时 15,000 点。

可以按以下章节所述来检查查询的点数值，或计算预期的点数值。 计算点数的公式和速率限制可能随时更改。

### 检查您的主速率限制的状态

可以使用随每个响应一起发送的标头来确定主要速率限制的当前状态。

| 标头名称                    | 说明                                          |
| ----------------------- | ------------------------------------------- |
| `x-ratelimit-limit`     | 每小时可以使用的最大点数                                |
| `x-ratelimit-remaining` | 当前速率限制窗口中剩余的点数                              |
| `x-ratelimit-used`      | 您在当前速率限制窗口中已使用的点数                           |
| `x-ratelimit-reset`     | 当前速率限制窗口重置的时间，单位为 UTC 纪元秒                   |
| `x-ratelimit-resource`  | 请求计数的速率限制资源。 对于 GraphQL 请求，这将始终为 `graphql`。 |

还还可以通过查询 `rateLimit` 对象来检查速率限制。 在可能的情况下，您应使用速率限制响应头，而不是访问 API 来检查速率限制信息。

```graphql
query {
  viewer {
    login
  }
  rateLimit {
    limit
    remaining
    used
    resetAt
  }
}
```

| 字段          | 说明                        |
| ----------- | ------------------------- |
| `limit`     | 每小时可以使用的最大点数              |
| `remaining` | 当前速率限制窗口中剩余的点数            |
| `used`      | 您在当前速率限制窗口中已使用的点数         |
| `resetAt`   | 当前速率限制窗口重置的时间，单位为 UTC 纪元秒 |

### 返回查询的点数值

可以通过查询 `cost` 对象上的 `rateLimit` 字段来返回查询的点数值：

```graphql
query {
  viewer {
    login
  }
  rateLimit {
    cost
  }
}
```

### 预测查询的点值

在进行查询之前，还可以大致计算查询的点数值。

1. 将完成调用中每个独有连接所需的请求数加起来。 假设每个请求都将达到 `first` 或 `last` 参数限制。
2. 将计算所得的数字除以 100\*\*\*\*，然后将结果四舍五入为最接近的整数，即可获取最终加总点数值。 这一步可使大数字规范化。

> \[!NOTE]
> 对 GraphQL API 的调用的最小点数值为 1\*\*\*\*。

下面是一个查询和分数计算示例：

```graphql
query {
  viewer {
    login
    repositories(first: 100) {
      edges {
        node {
          id

          issues(first: 50) {
            edges {
              node {
                id

                labels(first: 60) {
                  edges {
                    node {
                      id
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

此查询需要 5,101 个请求才能完成：

* 虽然我们要返回 100 个存储库，但 API 必须连接到查看器的帐户一次才能获取存储库列表。 因此，请求存储库的数量 = **1**
* 虽然我们要返回 50 个问题，但 API 必须与 100 个存储库的每个库相连接，才能获取问题列表。 因此，针对问题的请求数 = **100**
* 虽然我们要返回 60 个标签，但 API 必须与 5,000 个潜在总问题中的每个问题相连接，才能获取标签列表。 因此，针对标签的请求数 = **5000**
* 总计 = 5,101

除以100，然后四舍五入就得到了查询的最终分数：51

## 二级费率限制

除了主要速率限制以外，GitHub 还强制执行次要速率限制以阻止滥用，让 API 可供所有用户所使用。

可能会在以下情况中遇到二级速率限制：

* *发出的并发请求过多。* 并发请求数量不能超过 100 个。 REST API 和 GraphQL API 都应用此限制。
* *每分钟向单个终结点发出的请求数过多。* REST API 终结点每分钟允许发出的请求数不超过 900 点，GraphQL API 终结点每分钟允许发出的请求数不过超 2,000 点。 有关计分的详细信息，请参阅“[计算次要速率限制的点数](#calculating-points-for-the-secondary-rate-limit)”。
* *每分钟发出的请求数过多。* 实时每 60 秒允许的 CPU 时间不超过 90 秒。 此 CPU 时间最多可以用于 GraphQL API 的时间不能超过 60 秒。 可以通过衡量 API 请求的总响应时间来大致估算出 CPU 时间。
* *发出过多的请求，它们在短时间内会消耗过多的计算资源。*
* *短时间内在 GitHub 上创建的内容过多。* 一般情况下，每分钟不超过 80 个内容生成请求，允许每小时不超过 500 个内容生成请求。 某些终结点的内容创建限制较低。 内容创建限制包括在 GitHub 的 Web 界面以及通过 REST API 和 GraphQL API 进行的操作。
* *在短时间内发出过多的 OAuth 访问令牌请求。* 每小时对于 GitHub Apps 和 OAuth apps 的 OAuth 访问令牌请求不允许超过 2,000 次。

上述次要速率限制可能随时更改，恕不另行通知。 您可能会因为某些未公开的原因而遇到次级速率限制。

### 计算次要速率限制的点数

某些次要速率限制由请求的点值确定。 对于 GraphQL 请求，这些点值与主要速率限制的点值分开来进行计算。

| 请求                                              | 积分 |
| ----------------------------------------------- | -- |
| 不具有突变的 GraphQL 请求                               | 1  |
| 具有突变的 GraphQL 请求                                | 5  |
| 大多数 REST API `GET`、`HEAD` 和 `OPTIONS` 请求        | 1  |
| 大多数 REST API `POST`、`PATCH`、`PUT` 或 `DELETE` 请求 | 5  |

某些 REST API 终结点具有不公开共享的不同点成本。

## 超出速度限制

如果超出主要速率限制，响应状态仍将是 `200`，但会收到错误消息，并且 `x-ratelimit-remaining` 标头的值将为 `0`。 应在 `x-ratelimit-reset` 标头所指定的时间之后，再尝试发出请求。

如果超出次要速率限制，则响应状态将为 `200` 或 `403`，并显示一条错误消息，表明超出了了次要速率限制。 如果有 `retry-after` 响应头，则应先等待数秒，然后再尝试请求。 如果 `x-ratelimit-remaining` 标头为 `0`，请勿在 `x-ratelimit-reset` 标头指定的时间（UTC 纪元时间的秒）之前重试您的请求。 否则，请在重试之前等待至少一分钟。 如果您的请求因二级速率限制而持续失败，请在每次重试之间等待呈指数级增长的时间，并在达到特定重试次数后抛出一个错误。

如果在受到速率限制的情况下继续发出请求，可能会导致禁止集成。

## 保持在速率限制范围内

要避免超出速率限制，应在可变请求之间至少暂停 1 秒，并且不要发出并发请求。

此外还应订阅 Webhook 事件，而不是通过轮询 API 来获取数据。 有关详细信息，请参阅“[Webhooks 文档](/zh/webhooks)”。

还可以流式传输审核日志来查看 API 请求。 这有助于排查超出速率限制的集成问题。 有关详细信息，请参阅“[流式处理企业审核日志](/zh/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/streaming-the-audit-log-for-your-enterprise)”。

## 节点限制

若要通过 [schema](/zh/graphql/guides/introduction-to-graphql#schema) 验证，所有 GraphQL API [calls](/zh/graphql/guides/forming-calls-with-graphql)必须满足以下标准：

* 客户端必须在任何 `first` 上提供 `last` 或 [](/zh/graphql/guides/introduction-to-graphql#connection) 参数。
* `first` 和 `last` 的值必须在 1-100 以内。
* 单个调用不能请求超过 500,000 个[节点](/zh/graphql/guides/introduction-to-graphql#node)。

### 计算调用中的节点

下面两个示例显示如何计算调用中的节点总数。

1. 简单查询：

   <pre>query {
     viewer {
       repositories(first: <span class="redbox">50</span>) {

edges {
repository:node {
name

issues(first: <span class="greenbox">10</span>) {
totalCount
edges {
node {
title
bodyHTML
}
}
}
}
}
}
}
}</pre>

计算：

   <pre><span class="redbox">50</span>         = 50 repositories
    +
   <span class="redbox">50</span> x <span class="greenbox">10</span>  = 500 repository issues

= 550 total nodes</pre>

1. 复杂查询：

   <pre>query {
     viewer {
       repositories(first: <span class="redbox">50</span>) {

edges {
repository:node {
name

pullRequests(first: <span class="greenbox">20</span>) {
edges {
pullRequest:node {
title

comments(first: <span class="bluebox">10</span>) {
edges {
comment:node {
bodyHTML
}
}
}
}
}
}

issues(first: <span class="greenbox">20</span>) {
totalCount
edges {
issue:node {
title
bodyHTML

comments(first: <span class="bluebox">10</span>) {
edges {
comment:node {
bodyHTML
}
}
}
}
}
}
}
}
}

```
   followers(first: <span class="bluebox">10</span>) {
```

edges {
follower:node {
login
}
}
}
}
}</code></pre>

计算：

   <pre><span class="redbox">50</span>              = 50 repositories
    +
   <span class="redbox">50</span> x <span class="greenbox">20</span>       = 1,000 pullRequests
    +
   <span class="redbox">50</span> x <span class="greenbox">20</span> x <span class="bluebox">10</span> = 10,000 pullRequest comments
    +
   <span class="redbox">50</span> x <span class="greenbox">20</span>       = 1,000 issues
    +
   <span class="redbox">50</span> x <span class="greenbox">20</span> x <span class="bluebox">10</span> = 10,000 issue comments
    +
   <span class="bluebox">10</span>              = 10 followers

= 22,060 total nodes</pre>

## 超时

如果 GitHub 处理 API 请求需要 10 秒以上，将终止请求， GitHub 你将收到超时响应和一条消息，报告“我们无法及时响应你的请求”。

发生这种情况时，您可能会收到 `502` 或 `504` 状态代码。 这两个状态代码都表示你的请求已超时。

GitHub 保留更改超时窗口的权限，以保护 API 的速度和可靠性。

可以在 [githubstatus.com](https://www.githubstatus.com/) 上检查 GraphQL API 的状态，以确定超时是否是由于 API 出现问题而造成的。 也可以尝试简化请求，或者稍后尝试发出请求。 有关提高查询性能的提示，请参阅 [查询优化策略](#query-optimization-strategies)。

如果任何 API 请求发生超时，则在接下来的一小时内将从主要速率限制中扣除额外积分，以保护 API 的速度和可靠性。

## 其他资源限制

若要保护 API 的速度和可靠性， GitHub 还强制实施其他资源限制。 如果 GraphQL 查询消耗的资源过多， GitHub 将终止请求并返回部分结果，并返回指示超出资源限制的错误。

**可能超过资源限制的查询示例：**

* 在单个查询中请求数千个对象或深层嵌套关系。
* 同时在多个连接中使用大型 `first` 或 `last` 参数。
* 获取每个对象的大量详细信息，例如每个存储库的所有备注、回应和相关议题。

## 查询优化策略

* **限制对象数量**：对 `first` 或 `last` 参数使用较小值，并对结果分页。
* **减少查询深度**：除非必要，否则请避免请求深度嵌套对象。
* **筛选结果**：使用参数筛选数据并仅返回所需内容。
* **拆分大型查询**：将复杂查询拆分为多个更简单的查询。
* **仅请求必填字段**：仅选择所需字段，而不是请求所有可用字段。

通过遵循这些策略，可以降低达到资源限制的可能性，并提高 API 请求的性能和可靠性。