数据库故障排除和调试
本节提供了一些可以直接复制使用的代码片段,当您遇到令人头疼的数据库问题时,可以参考使用。
第一步是在 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:检查所有迁移是否为up或down状态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 上。为了能够从这些工具访问数据库,需要执行以下步骤:
-
在 GDK 根目录下运行:
gdk config set postgresql.host localhost -
打开您的
gdk.yml,并确认它包含以下行:postgresql: host: localhost -
重新配置 GDK:
gdk reconfigure -
在您的数据库 GUI 中,选择
localhost作为主机,5432作为端口,gitlabhq_development作为数据库。 您也可以使用连接字符串postgresql://localhost:5432/gitlabhq_development。
新的连接现在应该可以正常工作了。
使用 Visual Studio Code 访问 GDK 数据库
使用 Visual Studio Code 中的 PostgreSQL 扩展创建数据库连接,以访问和探索 GDK 数据库。
先决条件:
- Visual Studio (VS) Code。
- PostgreSQL VS Code 扩展。
要创建数据库连接:
-
在活动栏中,选择 PostgreSQL Explorer 图标。
-
从打开的面板中,选择 + 添加新的数据库连接:
-
输入数据库的 hostname。使用您 GDK 目录中 PostgreSQL 文件夹的路径。
- 示例:
/dev/gitlab-development-kit/postgresql
- 示例:
-
输入 PostgreSQL 用户身份验证。 除非在 PostgreSQL 安装期间另有说明,否则使用您的本地用户名。 要验证您的 PostgreSQL 用户名:
-
确保您在
gitlab目录中。 -
访问 PostgreSQL 数据库。运行
rails db。输出应该如下所示:psql (14.9) Type "help" for help. gitlabhq_development=# -
在返回的 PostgreSQL 提示符中,运行
\conninfo以显示连接的用户和 用于建立连接的端口。例如:You are connected to database "gitlabhq_development" as user "root" on host "localhost" (address "127.0.0.1") at port "5432".
-
-
当提示输入 PostgreSQL 用户的密码 时,输入您设置的密码或留空。
- 由于您登录到运行 PostgreSQL 服务器的同一台机器上,因此不需要密码。
-
输入 要连接的端口号。默认端口号是
5432。 -
在 使用 SSL 连接? 字段中,选择适合您安装的连接选项。选项有:
- 使用安全连接
- 标准连接(默认)
-
在可选的 要连接的数据库 字段中,输入
gitlabhq_development。 -
在 数据库连接的显示名称 字段中,输入
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 37709db: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