数据库负载均衡

  • 从专业版移动到基础版于 14.0 版本。
  • 为 Sidekiq 引入于 14.1 版本。

使用数据库负载均衡,只读查询可以分布在多个 PostgreSQL 节点上以提高性能。

此功能在 Rails 和 Sidekiq 中本地提供,可以将它们配置为以循环方式平衡其数据库读取查询,而无需任何外部依赖:

启用数据库负载均衡的要求

要启用数据库负载均衡,请确保:

  • HA PostgreSQL 设置具有一个或多个复制主节点的次要节点。
  • 每个 PostgreSQL 节点都使用相同的凭据和相同的端口连接。

对于 Linux 软件包安装,您还需要在每个 PostgreSQL 节点上配置 PgBouncer,以便在配置多节点设置时汇集所有负载均衡连接。

配置数据库负载均衡

可以通过以下两种方式之一配置数据库负载平衡:

  • (推荐)Hosts:PostgreSQL 主机列表。
  • 服务发现:返回 PostgreSQL 主机列表的 DNS 记录。

Hosts

要配置主机列表,在您要配置负载均衡的每个环境的所有 Rails (Sidekiq) 节点上执行以下步骤:

  1. 编辑 /etc/gitlab/gitlab.rb 文件。
  2. gitlab_rails['db_load_balancing'] 中,创建一个要负载均衡的只读副本数组。不要添加主要主机。例如,在主机 primary.example.comhost1.example.comhost2.example.comhost3.example.com 上运行 PostgreSQL 的环境中运行:

    gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] }
    

    这些副本必须可以在使用 gitlab_rails['db_port'] 配置的相同端口上访问。

  3. 保存文件并重新配置极狐GitLab

服务发现

服务发现允许极狐GitLab 自动检索要使用的 PostgreSQL 主机列表。它定期检查 DNS A 记录,使用此记录返回的 IP 作为辅助服务器的地址。要使服务发现工作,您只需要一个 DNS 服务器和一个包含辅助服务器 IP 地址的 A 记录。

使用 Linux 软件包安装时,提供的 Consul 服务用作 DNS 服务器,并通过 postgresql-ha.service.consul 记录返回 PostgreSQL 地址。例如:

  1. 在每个 Rails/Sidekiq 节点上,编辑 /etc/gitlab/gitlab.rb 并添加以下内容:
  gitlab_rails['db_load_balancing'] = { 'discover' => {
      'nameserver' => 'localhost'
      'record' => 'postgresql-ha.service.consul'
      'record_type' => 'A'
      'port' => '8600'
      'interval' => '60'
      'disconnect_timeout' => '120'
    }
  }
  1. 保存文件并重新配置极狐GitLab,使更改生效。
选项 描述 默认值
nameserver 用于查找 DNS 记录的名称服务器。 localhost
record 要查找的记录。此选项是服务发现工作所必需的。  
record_type 要查找的可选记录类型,可以是 ASRVA
port 名称服务器的端口。 8600
interval 检查 DNS 记录之间的最短时间(以秒为单位)。 60
disconnect_timeout 更新主机列表后关闭旧连接的时间(以秒为单位)。 120
use_tcp 使用 TCP 而不是 UDP 查找 DNS 资源。 false

如果 record_type 设置为 SRV,则极狐GitLab 继续使用循环算法并忽略记录中的 weightpriority。由于 SRV 记录通常返回主机名而不是 IP,极狐GitLab 需要在 SRV 响应的附加部分中查找返回的主机名的 IP。

如果没有找到主机名的 IP,极狐GitLab 需要为每个这样的主机名查询配置的 nameserverANY 记录,以寻找 AAAAA 记录,如果无法解析它的 IP,最终从轮替中删除此主机名。

interval 值指定检查之间的最短时间。如果 A 记录的 TTL 大于此值,则服务发现会尊重该 TTL。例如,如果 A 记录的 TTL 为 90 秒,那么服务发现在再次检查 A 记录之前至少等待 90 秒。

更新主机列表时,可能需要一段时间才能终止旧连接。disconnect_timeout 设置可用于强制终止所有旧数据库连接所需的时间上限。

处理过时的读取

从专业版移到基础版于 14.0 版本。

为了防止从过时的次要节点读取数据,负载均衡器会检查它是否与主节点同步。如果数据足够新,则使用次要节点数据,否则将被忽略。为了减少这些检查的开销,我们只在特定的时间间隔执行它们。

影响此行为的三个配置选项:

选项 描述 默认值
max_replication_difference 当次要节点有一段时间没有复制数据时,它允许滞后的数据量(以字节为单位)。 8 MB
max_replication_lag_time 在我们停止使用次要节点之前,它允许滞后的最大秒数。 60 seconds
replica_check_interval 在检查次要节点状态之前,我们必须等待的最小秒数。 60 seconds

对于大多数用户来说,默认值应该足够了。

要使用主机列表配置这些选项,请使用以下示例:

gitlab_rails['db_load_balancing'] = {
  'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`]
  'max_replication_difference' => 16777216 # 16 MB
  'max_replication_lag_time' => 30
  'replica_check_interval' => 30
}

日志记录

负载均衡器在 database_load_balancing.log 中记录各种事件,例如:

  • 当主机被标记为离线时
  • 当主机重新上线时
  • 当所有从属节点都离线时
  • 由于查询冲突而在不同主机上重试读取时

日志由每个条目构成一个 JSON 对象,其中至少包含:

  • 用于过滤的 event 字段。
  • 一个人类可读的 message 字段。
  • 一些特定于事件的元数据。例如,db_host
  • 始终记录的上下文信息。例如,severitytime

例如:

{"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null}

实施细节

均衡查询

只读 SELECT 查询所有给定主机之间的平衡。 其他所有内容(包括事务)都在主节点上执行。 诸如 SELECT ... FOR UPDATE 之类的查询也在主节点上执行。

准备好的语句

准备好的语句不适用于负载平衡,并且在启用负载平衡时会自动禁用。这不应该影响响应时间。

主节点粘结

执行写入后,极狐GitLab 会在一段时间内坚持使用主节点,范围仅限于执行写入的用户。极狐GitLab 在它们赶上或 30 秒后恢复使用次要节点。

故障转移处理

如果发生故障转移或数据库无响应,负载均衡器会尝试使用下一个可用主机。如果没有可用的次要节点,则改为在主节点上执行操作。

如果在写入数据时发生连接错误,该操作会使用指数回退重试最多 3 次。

使用负载均衡时,您应该能够安全地重新启动数据库服务器,而不会立即导致向用户显示错误。