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

数据库故障排除和调试

本节提供了一些可以直接复制使用的代码片段,当您遇到令人头疼的数据库问题时,可以参考使用。

第一步是在 Slack 中搜索您的错误,或者用 Google 搜索 GitLab <我的错误>

可用的 RAILS_ENV

  • production(通常不用于您的主 GDK 数据库,但您可能需要它用于其他安装,如 Omnibus)。
  • development(这是您的主 GDK 数据库)。
  • test(用于 RSpec 等测试)。

删除所有内容并重新开始

如果只是想删除所有内容并从空数据库开始(大约需要 1 分钟):

bundle exec rake db:reset RAILS_ENV=development

如果想用示例数据填充空数据库(大约需要 4 分钟):

bundle exec rake dev:setup

如果只是想删除所有内容并从示例数据开始(大约需要 4 分钟)。这也会执行 db:reset 并运行数据库特定的迁移:

bundle exec rake db:setup RAILS_ENV=development

如果您的测试数据库出现问题,删除所有内容也是安全的,因为它不包含重要数据:

bundle exec rake db:reset RAILS_ENV=test

迁移操作

  • bundle exec rake db:migrate RAILS_ENV=development:执行您可能从 MR 中获取的任何待处理迁移
  • bundle exec rake db:migrate:status RAILS_ENV=development:检查所有迁移是否为 updown 状态
  • bundle exec rake db:migrate:down:main VERSION=20170926203418 RAILS_ENV=development:回滚一个迁移
  • bundle exec rake db:migrate:up:main VERSION=20170926203418 RAILS_ENV=development:应用一个迁移
  • bundle exec rake db:migrate:redo:main VERSION=20170926203418 RAILS_ENV=development:重新运行特定迁移

将上述命令中的 main 替换为 ci,以对 ci 数据库而不是 main 执行操作。

手动访问数据库

使用以下命令之一访问数据库。它们都能到达同一个地方。

gdk psql -d gitlabhq_development
bundle exec rails dbconsole -e development
bundle exec rails db -e development
  • \q:退出
  • \dt:列出所有表
  • \d+ issues:列出 issues 表的列
  • CREATE TABLE board_labels();:创建一个名为 board_labels 的表
  • SELECT * FROM schema_migrations WHERE version = '20170926203418';:检查是否运行了某个迁移
  • DELETE FROM schema_migrations WHERE version = '20170926203418';:手动删除一个迁移记录

使用 GUI 访问数据库

大多数 GUI(DataGrip、RubyMine、DBeaver)需要到数据库的 TCP 连接,但默认情况下数据库运行在 UNIX socket 上。为了能够从这些工具访问数据库,需要执行以下步骤:

  1. 在 GDK 根目录下运行:

    gdk config set postgresql.host localhost
  2. 打开您的 gdk.yml,并确认它包含以下行:

    postgresql:
       host: localhost
  3. 重新配置 GDK:

    gdk reconfigure
  4. 在您的数据库 GUI 中,选择 localhost 作为主机,5432 作为端口,gitlabhq_development 作为数据库。 您也可以使用连接字符串 postgresql://localhost:5432/gitlabhq_development

新的连接现在应该可以正常工作了。

使用 Visual Studio Code 访问 GDK 数据库

使用 Visual Studio Code 中的 PostgreSQL 扩展创建数据库连接,以访问和探索 GDK 数据库。

先决条件:

要创建数据库连接:

  1. 在活动栏中,选择 PostgreSQL Explorer 图标。

  2. 从打开的面板中,选择 + 添加新的数据库连接:

  3. 输入数据库的 hostname。使用您 GDK 目录中 PostgreSQL 文件夹的路径。

    • 示例:/dev/gitlab-development-kit/postgresql
  4. 输入 PostgreSQL 用户身份验证。 除非在 PostgreSQL 安装期间另有说明,否则使用您的本地用户名。 要验证您的 PostgreSQL 用户名:

    1. 确保您在 gitlab 目录中。

    2. 访问 PostgreSQL 数据库。运行 rails db。输出应该如下所示:

      psql (14.9)
      Type "help" for help.
      
      gitlabhq_development=#
    3. 在返回的 PostgreSQL 提示符中,运行 \conninfo 以显示连接的用户和 用于建立连接的端口。例如:

      You are connected to database "gitlabhq_development" as user "root" on host "localhost" (address "127.0.0.1") at port "5432".
  5. 当提示输入 PostgreSQL 用户的密码 时,输入您设置的密码或留空。

    • 由于您登录到运行 PostgreSQL 服务器的同一台机器上,因此不需要密码。
  6. 输入 要连接的端口号。默认端口号是 5432

  7. 使用 SSL 连接? 字段中,选择适合您安装的连接选项。选项有:

    • 使用安全连接
    • 标准连接(默认)
  8. 在可选的 要连接的数据库 字段中,输入 gitlabhq_development

  9. 数据库连接的显示名称 字段中,输入 gitlabhq_development

您的 gitlabhq_development 数据库连接现在显示在 PostgreSQL Explorer 面板中。 使用箭头展开和探索 GDK 数据库的内容。

如果无法连接,请首先确保 GDK 正在运行,然后重试。有关如何使用 VS Code 的 PostgreSQL Explorer 扩展的进一步说明,请参阅 扩展文档的使用部分

常见问题

使用 Spring 时出现 ActiveRecord::PendingMigrationError

当使用 Spring 预加载器 运行规范时,测试数据库可能会进入损坏状态。尝试运行迁移或删除/重置测试数据库没有效果。

$ bundle exec spring rspec some_spec.rb
...
Failure/Error: ActiveRecord::Migration.maintain_test_schema!

ActiveRecord::PendingMigrationError:


  Migrations are pending. To resolve this issue, run:

    bin/rake db:migrate RAILS_ENV=test
# ~/.rvm/gems/ruby-2.3.3/gems/activerecord-4.2.10/lib/active_record/migration.rb:392:in `check_pending!'
...
0 examples, 0 failures, 1 error occurred outside of examples

要解决此问题,您可以终止在规范运行之间存在的 spring 服务器和应用程序。

$ ps aux | grep spring
eric             87304   1.3  2.9  3080836 482596   ??  Ss   10:12AM   4:08.36 spring app    | gitlab | started 6 hours ago | test mode
eric             37709   0.0  0.0  2518640   7524 s006  S    Wed11AM   0:00.79 spring server | gitlab | started 29 hours ago
$ kill 87304
$ kill 37709

db:migrate database version is too old to be migrated 错误

db:migrate 检测到当前架构版本早于 Gitlab::Database 库模块中定义的 MIN_SCHEMA_VERSION 时,用户会收到此错误。

随着时间的推移,我们在代码库中清理/合并了旧的迁移,因此并不总是可以从每个以前的版本迁移 GitLab。

在某些情况下,您可能希望绕过此检查。例如,如果您使用的 GitLab 架构版本晚于 MIN_SCHEMA_VERSION,然后回滚到更早的迁移,在这种情况下,要再次向前迁移,您应该设置 SKIP_SCHEMA_VERSION_CHECK 环境变量。

bundle exec rake db:migrate SKIP_SCHEMA_VERSION_CHECK=true

性能问题

使用连接池减少连接开销

创建新的数据库连接并非没有成本,特别是在 PostgreSQL 中,它需要为每个新连接分叉一个完整进程。如果连接存在很长时间,这没有问题。但是,为几个小查询分叉一个进程可能会变得很昂贵。如果不加以处理,新的数据库连接峰值可能导致性能下降,甚至导致完全中断。

对于处理大量短暂数据库连接激增的实例,经过验证的解决方案是实现 PgBouncer 作为连接池。这个池可以容纳数千个连接,几乎没有任何开销。缺点是增加少量延迟,以换取高达 90% 以上的性能提升,具体取决于使用模式。

可以微调 PgBouncer 以适应不同的安装。有关更多信息,请参阅我们关于微调 PgBouncer 的文档。

运行 ANALYZE 重新生成数据库统计信息

ANALYZE 命令是解决许多性能问题的良好第一步。通过重新生成表统计信息,查询规划器创建了更高效的查询执行路径。

最新的统计信息永远不会有害!

  • 对于 Linux 包,运行:

    gitlab-psql -c 'SET statement_timeout = 0; ANALYZE VERBOSE;'
  • 在 SQL 提示符下,运行:

    -- 因为这可能会运行超过默认的 statement_timeout
    SET statement_timeout = 0;
    ANALYZE VERBOSE;

收集 ACTIVE 工作负载的数据

活动查询是唯一实际消耗数据库大量资源的查询。

此查询收集所有现有活动查询的元信息,包括:

  • 它们的年龄
  • 源服务
  • wait_event(如果处于等待状态)
  • 其他可能相关的信息:
-- 长查询通常垂直排列字段时更容易阅读
\x

SELECT
    pid
    ,datname
    ,usename
    ,application_name
    ,client_hostname
    ,backend_start
    ,query_start
    ,query
    ,age(now(), query_start) AS "age"
    ,state
    ,wait_event
    ,wait_event_type
    ,backend_type
FROM pg_stat_activity
WHERE state = 'active';

此查询捕获单个快照,因此在环境无响应时,考虑在几分钟内运行查询 3-5 次:

-- 将输出重定向到文件
-- 此位置必须可由 `gitlab-psql` 写入
\o /tmp/active1304.out
--
-- 现在执行上面的查询
--
-- 所有输出都写入文件 - 如果提示符是 = 则表示已运行
-- 取消写入输出
\o

这个 Python 脚本 可以帮助您将 pg_stat_activity 的输出解析为更容易理解并与性能问题关联的数字。

调查看起来很慢的查询

当您发现某个查询花费太长时间完成,或者占用太多数据库资源时,使用 EXPLAIN 检查查询规划器如何执行它:

EXPLAIN (ANALYZE, BUFFERS) SELECT ... FROM ...

BUFFERS 还显示了大约涉及多少内存。I/O 可能导致问题,因此确保在运行 EXPLAIN 时添加 BUFFERS

如果数据库有时性能良好,有时缓慢,请在环境处于任一状态时捕获相同查询的此输出。

调查索引膨胀

索引膨胀通常不应导致明显的性能问题,但它可能导致高磁盘使用率,特别是当存在自动清理问题时。

下面的查询从 PostgreSQL 自身的 postgres_index_bloat_estimates 表计算膨胀百分比,并按百分比值对结果进行排序。 PostgreSQL 需要一定量的膨胀才能正确运行,因此大约 25% 仍然代表标准行为。

select  a.identifier, a.bloat_size_bytes, b.tablename, b.ondisk_size_bytes,
    (a.bloat_size_bytes/b.ondisk_size_bytes::float)*100 as percentage
from postgres_index_bloat_estimates a
join postgres_indexes b on a.identifier=b.identifier
where
   -- 确保百分比计算不会遇到零
   a.bloat_size_bytes>0 and
   b.ondisk_size_bytes>1000000000
order by  percentage desc;

重建索引

如果您识别出膨胀的表,可以使用下面的查询重建其索引。之后您也应该重新运行 ANALYZE,因为索引重建后统计信息可能会被重置。

SET statement_timeout = 0;
REINDEX TABLE CONCURRENTLY <table_name>;

通过在分号后添加 \watch 30 运行下面的查询来监控索引重建过程:

SELECT
  t.tablename, indexname, c.reltuples AS num_rows,
  pg_size_pretty(pg_relation_size(quote_ident(t.tablename)::text)) AS table_size,
  pg_size_pretty(pg_relation_size(quote_ident(indexrelname)::text)) AS index_size,
CASE WHEN indisvalid THEN 'Y'
  ELSE 'N'
END AS VALID
FROM pg_tables t
LEFT OUTER JOIN pg_class c ON t.tablename=c.relname
LEFT OUTER JOIN
  ( SELECT c.relname AS ctablename, ipg.relname AS indexname, x.indnatts AS
  number_of_columns, indexrelname, indisvalid FROM pg_index x
JOIN pg_class c ON c.oid = x.indrelid
JOIN pg_class ipg ON ipg.oid = x.indexrelid
JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid )
AS foo
ON t.tablename = foo.ctablename
WHERE
  t.tablename in ('<comma_separated_table_names>')
  ORDER BY 1,2; \watch 30