- 모든 내용을 삭제하고 처음부터 시작하기
- 마이그레이션 조정
- 데이터베이스에 수동으로 접근하기
- GUI로 데이터베이스 접근하기
- Visual Studio Code로 GDK 데이터베이스에 접근하기
- FAQ
- 성능 문제
데이터베이스 문제 해결 및 디버깅
이 섹션은 머리에서 쿵 소리가 나는 데이터베이스 문제를 만났을 때 참고할 수 있는 복사-붙여넣기 내용을 제공하는 데 도움을 줍니다.
첫 번째 단계는 Slack에서 오류를 검색하거나 GitLab <내 오류>
로 Google에서 검색하는 것입니다.
사용 가능한 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
데이터베이스에 대해 실행하려면 바꿉니다.
데이터베이스에 수동으로 접근하기
다음 명령 중 하나로 데이터베이스에 접근합니다. 이 모든 명령은 동일한 위치로 이동합니다.
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 데이터베이스에 접근하고 탐색합니다.
전제 조건:
- 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) 도움말은 "help"를 입력하세요. gitlabhq_development=#
-
반환된 PostgreSQL 프롬프트에서
\conninfo
를 실행하여 연결된 사용자와 연결을 설정하는 데 사용된 포트를 표시합니다. 예를 들어:데이터베이스 "gitlabhq_development"에 사용자 "root"로 호스트 "localhost" (주소 "127.0.0.1")에서 포트 "5432"로 연결되어 있습니다.
-
-
PostgreSQL 사용자 비밀번호를 입력하라는 메시지가 나오면 설정한 비밀번호를 입력하거나 필드를 비워둡니다.
- Postgres 서버가 실행 중인 동일한 머신에 로그인 중이므로 비밀번호는 필요하지 않습니다.
- 연결할 포트 번호를 입력합니다. 기본 포트 번호는
5432
입니다. -
SSL 연결을 사용할까요? 필드에서 설치에 적합한 연결을 선택합니다. 옵션은 다음과 같습니다:
- 안전한 연결 사용
- 표준 연결 (기본값)
- 선택적 연결할 데이터베이스 필드에
gitlabhq_development
를 입력합니다. -
데이터베이스 연결에 대한 표시 이름 필드에
gitlabhq_development
를 입력합니다.
이제 gitlabhq_development
데이터베이스 연결이 PostgreSQL Explorer 창에 표시됩니다.
화살표를 사용하여 GDK 데이터베이스의 내용을 확장하고 탐색합니다.
연결할 수 없는 경우, 먼저 GDK가 실행 중인지 확인하고 다시 시도하십시오. VS Code의 PostgreSQL Explorer 확장을 사용하는 방법에 대한 추가 지침은 사용 섹션에서 확인하십시오.
FAQ
ActiveRecord::PendingMigrationError
with Spring
Spring pre-loader와 함께 사양을 실행할 때 테스트 데이터베이스가 손상된 상태가 될 수 있습니다. 마이그레이션을 실행하거나 테스트 데이터베이스를 삭제/재설정해도 효과가 없습니다.
$ bundle exec spring rspec some_spec.rb
...
실패/오류: ActiveRecord::Migration.maintain_test_schema!
ActiveRecord::PendingMigrationError:
마이그레이션이 보류 중입니다. 이 문제를 해결하려면 다음을 실행하세요:
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 예제, 0 실패, 1 예외가 발생했습니다.
해결하려면 스펙 실행 간에 살아있는 스프링 서버와 앱을 종료할 수 있습니다.
$ 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을 모든 이전 버전에서 마이그레이션하는 것이 항상 가능하지는 않습니다.
어떤 경우에는 이 검사를 우회하고 싶을 수 있습니다. 예를 들어, 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
명령은 많은 성능 문제를 해결하기 위한 좋은 첫 번째 접근 방식입니다. 테이블 통계를 재생성함으로써 쿼리 플래너는 보다 효율적인 쿼리 실행 경로를 생성합니다.
최신 통계는 결코 해가 되지 않습니다!
-
리눅스 패키지의 경우 실행:
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
-- 비율 계산 시 0이 아닌지 확인
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