InfoGrab DocsInfoGrab Docs

데이터베이스 트러블슈팅 및 디버깅

요약

이 섹션은 데이터베이스 관련 문제가 발생했을 때 참고할 수 있는 복사-붙여넣기 가능한 내용을 제공합니다. 첫 번째 단계로 Slack에서 오류를 검색하거나, Google에서 GitLab <내 오류>로 검색하세요.

이 섹션은 데이터베이스 관련 문제가 발생했을 때 참고할 수 있는 복사-붙여넣기 가능한 내용을 제공합니다.

첫 번째 단계로 Slack에서 오류를 검색하거나, Google에서 GitLab <내 오류>로 검색하세요.

사용 가능한 RAILS_ENV:

  • production (일반적으로 GDK의 메인 데이터베이스에는 사용하지 않지만, Omnibus 같은 다른 설치 환경에서는 필요할 수 있습니다).

  • development (GDK의 메인 DB입니다).

  • test (RSpec 등의 테스트에 사용됩니다).

모두 삭제하고 새로 시작하기#

빈 DB로 모두 삭제하고 새로 시작하려면 (약 1분):

bundle exec rake db:reset RAILS_ENV=development

빈 DB에 샘플 데이터를 시딩하려면 (약 4분):

bundle exec rake dev:setup

샘플 데이터와 함께 모두 삭제하고 새로 시작하려면 (약 4분). 이 명령은 db:reset도 실행하며 DB 전용 마이그레이션도 수행합니다:

bundle exec rake db:setup RAILS_ENV=development

테스트 DB에 문제가 있는 경우, 중요한 데이터가 포함되어 있지 않으므로 안전하게 모두 삭제할 수 있습니다:

bundle exec rake db:reset RAILS_ENV=test

마이그레이션 관리#

  • bundle exec rake db:migrate RAILS_ENV=development: 머지 리퀘스트에서 가져온 미적용 마이그레이션을 실행합니다.

  • 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을 다른 이름으로 바꾸면 main 대신 ci 데이터베이스를 대상으로 실행할 수 있습니다.

데이터베이스에 수동으로 접근하기#

다음 명령 중 하나로 데이터베이스에 접근하세요. 모두 동일한 결과를 제공합니다.

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 소켓에서 실행됩니다. 이러한 도구에서 데이터베이스에 접근하려면 몇 가지 단계가 필요합니다:

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 데이터베이스를 탐색할 수 있습니다.

전제 조건:

데이터베이스 연결을 생성하려면:

활동 표시줄에서 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 사용자의 비밀번호를 입력하라는 메시지가 표시되면 설정한 비밀번호를 입력하거나 빈 칸으로 두세요.

Postgres 서버가 실행 중인 동일한 머신에 로그인되어 있으므로 비밀번호가 필요하지 않습니다.

연결할 포트 번호를 입력하세요. 기본 포트 번호는 5432입니다.

SSL 연결을 사용하시겠습니까? 필드에서 설치 환경에 맞는 연결을 선택하세요. 옵션은 다음과 같습니다:

보안 연결 사용

  • 표준 연결 (기본값)

선택 사항인 연결할 데이터베이스 필드에 gitlabhq_development를 입력하세요.

데이터베이스 연결의 표시 이름 필드에 gitlabhq_development를 입력하세요.

이제 gitlabhq_development 데이터베이스 연결이 PostgreSQL Explorer 패널에 표시됩니다. 화살표를 사용하여 GDK 데이터베이스의 내용을 펼치고 탐색하세요.

연결할 수 없는 경우 GDK가 실행 중인지 확인하고 다시 시도하세요. VS Code용 PostgreSQL Explorer 확장 사용 방법에 대한 자세한 내용은 확장의 문서에서 사용 섹션을 참조하세요.

FAQ#

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 오류: 데이터베이스 버전이 너무 오래되어 마이그레이션할 수 없습니다#

db:migrate가 현재 스키마 버전이 Gitlab::Database 라이브러리 모듈에 정의된 MIN_SCHEMA_VERSION보다 오래된 것을 감지하면 사용자는 이 오류를 받게 됩니다.

시간이 지남에 따라 코드베이스에서 오래된 마이그레이션을 정리/통합하므로, 이전 모든 버전에서 GitLab을 마이그레이션하는 것이 항상 가능한 것은 아닙니다.

일부 경우에는 이 검사를 우회하고 싶을 수 있습니다. 예를 들어, MIN_SCHEMA_VERSION보다 이후의 GitLab 스키마 버전에 있다가 이전의 더 오래된 마이그레이션으로 롤백한 경우입니다. 이 경우 다시 앞으로 마이그레이션하려면 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 프롬프트에서 다음을 실행하세요:

-- needed because this is likely to run longer than the default statement_timeout
SET statement_timeout = 0;
ANALYZE VERBOSE;

ACTIVE 워크로드 데이터 수집#

활성 쿼리만이 데이터베이스에서 상당한 리소스를 실제로 소비합니다.

이 쿼리는 모든 기존 활성 쿼리에서 메타 정보를 수집하며 다음을 포함합니다:

  • 쿼리 경과 시간

  • 출처 서비스

  • wait_event (대기 중인 상태인 경우)

  • 기타 관련 가능한 정보:

-- long queries are usually easier to read with the fields arranged vertically
\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회 실행하는 것을 고려하세요:

-- redirect output to a file
-- this location must be writable by `gitlab-psql`
\o /tmp/active1304.out
--
-- now execute the query above
--
-- all output goes to the file - if the prompt is = then it ran
-- cancel writing output
\o

이 Python 스크립트pg_stat_activity의 출력을 이해하기 쉽고 성능 이슈와 연관 짓기 쉬운 숫자로 파싱하는 데 도움이 될 수 있습니다.

느린 것처럼 보이는 쿼리 조사하기#

쿼리가 완료되는 데 너무 오래 걸리거나 데이터베이스 리소스를 너무 많이 소비하는 것을 발견하면, EXPLAIN으로 쿼리 플래너가 쿼리를 어떻게 실행하는지 확인하세요:

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

BUFFERS는 관련된 메모리 양도 대략적으로 보여줍니다. I/O가 문제를 일으킬 수 있으므로 EXPLAIN을 실행할 때 BUFFERS를 추가해야 합니다.

데이터베이스가 때로는 성능이 좋고 때로는 느린 경우, 환경이 각 상태에 있을 때 동일한 쿼리에 대한 이 출력을 캡처하세요.

인덱스 블로트 조사하기#

인덱스 블로트는 일반적으로 눈에 띄는 성능 문제를 일으키지는 않지만, 특히 autovacuum 이슈가 있는 경우 높은 디스크 사용량으로 이어질 수 있습니다.

아래 쿼리는 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
   -- to ensure the percentage calculation doesn't encounter zeroes
   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

데이터베이스 트러블슈팅 및 디버깅

GitLab v19.1
원문 보기
요약

이 섹션은 데이터베이스 관련 문제가 발생했을 때 참고할 수 있는 복사-붙여넣기 가능한 내용을 제공합니다. 첫 번째 단계로 Slack에서 오류를 검색하거나, Google에서 GitLab &#x3C;내 오류>로 검색하세요.

이 섹션은 데이터베이스 관련 문제가 발생했을 때 참고할 수 있는 복사-붙여넣기 가능한 내용을 제공합니다.

첫 번째 단계로 Slack에서 오류를 검색하거나, Google에서 GitLab <내 오류>로 검색하세요.

사용 가능한 RAILS_ENV:

  • production (일반적으로 GDK의 메인 데이터베이스에는 사용하지 않지만, Omnibus 같은 다른 설치 환경에서는 필요할 수 있습니다).

  • development (GDK의 메인 DB입니다).

  • test (RSpec 등의 테스트에 사용됩니다).

모두 삭제하고 새로 시작하기#

빈 DB로 모두 삭제하고 새로 시작하려면 (약 1분):

bundle exec rake db:reset RAILS_ENV=development

빈 DB에 샘플 데이터를 시딩하려면 (약 4분):

bundle exec rake dev:setup

샘플 데이터와 함께 모두 삭제하고 새로 시작하려면 (약 4분). 이 명령은 db:reset도 실행하며 DB 전용 마이그레이션도 수행합니다:

bundle exec rake db:setup RAILS_ENV=development

테스트 DB에 문제가 있는 경우, 중요한 데이터가 포함되어 있지 않으므로 안전하게 모두 삭제할 수 있습니다:

bundle exec rake db:reset RAILS_ENV=test

마이그레이션 관리#

  • bundle exec rake db:migrate RAILS_ENV=development: 머지 리퀘스트에서 가져온 미적용 마이그레이션을 실행합니다.

  • 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을 다른 이름으로 바꾸면 main 대신 ci 데이터베이스를 대상으로 실행할 수 있습니다.

데이터베이스에 수동으로 접근하기#

다음 명령 중 하나로 데이터베이스에 접근하세요. 모두 동일한 결과를 제공합니다.

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 소켓에서 실행됩니다. 이러한 도구에서 데이터베이스에 접근하려면 몇 가지 단계가 필요합니다:

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 데이터베이스를 탐색할 수 있습니다.

전제 조건:

데이터베이스 연결을 생성하려면:

활동 표시줄에서 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 사용자의 비밀번호를 입력하라는 메시지가 표시되면 설정한 비밀번호를 입력하거나 빈 칸으로 두세요.

Postgres 서버가 실행 중인 동일한 머신에 로그인되어 있으므로 비밀번호가 필요하지 않습니다.

연결할 포트 번호를 입력하세요. 기본 포트 번호는 5432입니다.

SSL 연결을 사용하시겠습니까? 필드에서 설치 환경에 맞는 연결을 선택하세요. 옵션은 다음과 같습니다:

보안 연결 사용

  • 표준 연결 (기본값)

선택 사항인 연결할 데이터베이스 필드에 gitlabhq_development를 입력하세요.

데이터베이스 연결의 표시 이름 필드에 gitlabhq_development를 입력하세요.

이제 gitlabhq_development 데이터베이스 연결이 PostgreSQL Explorer 패널에 표시됩니다. 화살표를 사용하여 GDK 데이터베이스의 내용을 펼치고 탐색하세요.

연결할 수 없는 경우 GDK가 실행 중인지 확인하고 다시 시도하세요. VS Code용 PostgreSQL Explorer 확장 사용 방법에 대한 자세한 내용은 확장의 문서에서 사용 섹션을 참조하세요.

FAQ#

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 오류: 데이터베이스 버전이 너무 오래되어 마이그레이션할 수 없습니다#

db:migrate가 현재 스키마 버전이 Gitlab::Database 라이브러리 모듈에 정의된 MIN_SCHEMA_VERSION보다 오래된 것을 감지하면 사용자는 이 오류를 받게 됩니다.

시간이 지남에 따라 코드베이스에서 오래된 마이그레이션을 정리/통합하므로, 이전 모든 버전에서 GitLab을 마이그레이션하는 것이 항상 가능한 것은 아닙니다.

일부 경우에는 이 검사를 우회하고 싶을 수 있습니다. 예를 들어, MIN_SCHEMA_VERSION보다 이후의 GitLab 스키마 버전에 있다가 이전의 더 오래된 마이그레이션으로 롤백한 경우입니다. 이 경우 다시 앞으로 마이그레이션하려면 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 프롬프트에서 다음을 실행하세요:

-- needed because this is likely to run longer than the default statement_timeout
SET statement_timeout = 0;
ANALYZE VERBOSE;

ACTIVE 워크로드 데이터 수집#

활성 쿼리만이 데이터베이스에서 상당한 리소스를 실제로 소비합니다.

이 쿼리는 모든 기존 활성 쿼리에서 메타 정보를 수집하며 다음을 포함합니다:

  • 쿼리 경과 시간

  • 출처 서비스

  • wait_event (대기 중인 상태인 경우)

  • 기타 관련 가능한 정보:

-- long queries are usually easier to read with the fields arranged vertically
\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회 실행하는 것을 고려하세요:

-- redirect output to a file
-- this location must be writable by `gitlab-psql`
\o /tmp/active1304.out
--
-- now execute the query above
--
-- all output goes to the file - if the prompt is = then it ran
-- cancel writing output
\o

이 Python 스크립트pg_stat_activity의 출력을 이해하기 쉽고 성능 이슈와 연관 짓기 쉬운 숫자로 파싱하는 데 도움이 될 수 있습니다.

느린 것처럼 보이는 쿼리 조사하기#

쿼리가 완료되는 데 너무 오래 걸리거나 데이터베이스 리소스를 너무 많이 소비하는 것을 발견하면, EXPLAIN으로 쿼리 플래너가 쿼리를 어떻게 실행하는지 확인하세요:

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

BUFFERS는 관련된 메모리 양도 대략적으로 보여줍니다. I/O가 문제를 일으킬 수 있으므로 EXPLAIN을 실행할 때 BUFFERS를 추가해야 합니다.

데이터베이스가 때로는 성능이 좋고 때로는 느린 경우, 환경이 각 상태에 있을 때 동일한 쿼리에 대한 이 출력을 캡처하세요.

인덱스 블로트 조사하기#

인덱스 블로트는 일반적으로 눈에 띄는 성능 문제를 일으키지는 않지만, 특히 autovacuum 이슈가 있는 경우 높은 디스크 사용량으로 이어질 수 있습니다.

아래 쿼리는 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
   -- to ensure the percentage calculation doesn't encounter zeroes
   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