Help us learn about your current experience with the documentation. Take the survey.

数据库负载均衡

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab Self-Managed

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

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

@startuml
!theme plain
card "**Internal Load Balancer**" as ilb
skinparam linetype ortho

together {
  collections "**GitLab Rails** x3" as gitlab
  collections "**Sidekiq** x4" as sidekiq
}

collections "**Consul** x3" as consul

card "Database" as database {
  collections "**PGBouncer x3**\n//Consul//" as pgbouncer

  card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary
  collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary

  pgbouncer --> postgres_primary
  postgres_primary .r-> postgres_secondary
}

gitlab --> ilb
gitlab -[hidden]-> pgbouncer
gitlab .[norank]-> postgres_primary
gitlab .[norank]-> postgres_secondary

sidekiq --> ilb
sidekiq -[hidden]-> pgbouncer
sidekiq .[norank]-> postgres_primary
sidekiq .[norank]-> postgres_secondary

ilb --> pgbouncer

consul -r-> pgbouncer
consul .[norank]r-> postgres_primary
consul .[norank]r-> postgres_secondary
@enduml

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

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

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

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

配置数据库负载均衡

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

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

主机

要配置主机列表,请在所有 GitLab Rails 和 Sidekiq 节点上为您想要平衡的每个环境执行以下步骤:

  1. 编辑 /etc/gitlab/gitlab.rb 文件。

  2. gitlab_rails['db_load_balancing'] 中,创建您想要平衡的数据库主机数组。例如,在 PostgreSQL 运行在主机 primary.example.comsecondary1.example.comsecondary2.example.com 上的环境中:

    gitlab_rails['db_load_balancing'] = { 'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.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. 在每个 GitLab 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'
      }
    }
  2. 保存文件并重新配置 GitLab 以使更改生效。

选项 描述 默认值
nameserver 用于查找 DNS 记录的名称服务器。 localhost
record 要查找的记录。此选项是服务发现工作所必需的。
record_type 要查找的可选记录类型。可以是 ASRV A
port 名称服务器的端口。 8600
interval 检查 DNS 记录之间的最小时间(秒)。 60
disconnect_timeout 主机列表更新后,旧连接关闭的时间(秒)。 120
use_tcp 使用 TCP 而不是 UDP 查找 DNS 资源 false
max_replica_pools 每个 Rails 进程连接到的最大副本数。如果您运行大量的 Postgres 副本和大量的 Rails 进程,这很有用,因为如果没有此限制,默认情况下每个 Rails 进程都会连接到每个副本。如果未设置,默认行为是无限制的。 nil

如果 record_type 设置为 SRV,则 GitLab 继续使用轮询算法并忽略记录中的 weightpriority。因为 SRV 记录通常返回主机名而不是 IP,GitLab 需要在 SRV 响应的附加部分中查找返回主机名的 IP。如果找不到主机名的 IP,GitLab 需要查询配置的 nameserver 以获取每个此类主机名的 ANY 记录,查找 AAAAA 记录,如果无法解析其 IP,则最终从轮换中删除此主机名。

interval 值指定检查之间的最小时间。如果 A 记录的 TTL 大于此值,则服务发现遵守所述 TTL。例如,如果 A 记录的 TTL 为 90 秒,则服务发现至少等待 90 秒才再次检查 A 记录。

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

处理过时读取

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

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

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

默认值应该足以满足大多数用户的需求。

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

gitlab_rails['db_load_balancing'] = {
  'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.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 在特定时间内坚持使用主节点,范围限定为执行写入的用户。当辅助节点赶上或 30 秒后,GitLab 恢复使用辅助节点。

故障转移处理

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

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

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

开发指南

有关数据库负载均衡的详细开发指南,请参阅开发文档。