PostgreSQL 9.5.4 문서 | |||
---|---|---|---|
이전 | 위로 | 장 27. 데이터베이스 성능 모니터링 | 다음 |
PostgreSQL은 데이터 서버의 dynamic tracing을 지원하는 기능을 제공한다. 코드 특정 지점에서 호출된 외부 유틸리티로 dynamic tracing을 한다.
프로브probe 혹은 trace point는 소스 코드에 이미 삽입돼 있는 경우가 많다. 프로브는 데이터베이스 개발자와 관리자들이 사용하도록 되어 있다. 기본적으로 프로브는 PostgreSQL로 컴파일 되지 않는다. 사용자는 설정 스크립트에 프로브를 컴파일 하도록 명시해야 한다.
현재 DTrace 유틸리티가 지원되고 있는데, 이 다큐멘테이션이 작성되는 시점에는 Solaris, mac OSX, FreeBSD, NetBSD, Oracle Linux에서 사용 가능하다. 리눅스의 SystemTap 프로젝트로 DTrace와 비슷한 유틸리티를 사용할 수 있다. 이 외의 dynamic tracing 유틸리티는 src/include/utils/probes.h에 있는 매크로 정의를 변경하여 사용할 수 있다.
기본적으로 프로브는 사용하지 않으므로, 설정 스크립트에 PostgreSQL이 프로브를 사용하도록 명시해 주어야 한다. DTrace를 활성화 하기 위해서 --enable-dtrace를 명시해 주어야 한다(자세한 정보는 15.4절을 참조).
대부분의 표준 프로브는 표 27-18처럼 소스 코드에서 제공된다. 표 27-19에서 프로브에 사용된 타입을 볼 수 있다. 프로브가 많을수록 PostgreSQL의 가관측성observability을 높일 수 있다.
표 27-18. 내장된 DTrace 프로브
Name | Parameters | 설명 |
---|---|---|
transaction-start | (LocalTransactionId) | 새로운 트랜잭션이 시작하는 지점에서 발생하는 프로브. 인자는 트랜잭션 ID |
transaction-commit | (LocalTransactionId) | 트랜잭션이 성공적으로 완료할 때 발생하는 프로브. 인자는 트랜잭션 ID |
transaction-abort | (LocalTransactionId) | 트랜잭션이 성공적으로 완료되지 않았을 때 발생하는 프로브. 인자는 트랜잭션 ID |
query-start | (const char *) | 쿼리 처리가 시작됐을 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-done | (const char *) | 쿼리 처리가 끝났을 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-parse-start | (const char *) | 쿼리 파싱이 시작됐을 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-parse-done | (const char *) | 쿼리 파싱이 끝났을 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-rewrite-start | (const char *) | 쿼리를 다시 쓰기 시작할 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-rewrite-done | (const char *) | 쿼리 다시 쓰기가 완료됐을 때 발생하는 프로브. 인자는 쿼리 문자열 |
query-plan-start | () | 쿼리 플래닝이 시작됐을 때 발생하는 프로브 |
query-plan-done | () | 쿼리 플래닝이 끝났을 때 발생하는 프로브 |
query-execute-start | () | 쿼리 수행이 시작됐을 때 발생하는 프로브 |
query-execute-done | () | 쿼리 수행이 끝났을 때 발생하는 프로브 |
statement-status | (const char *) | 서버 프로세스가 pg_stat_activity.status를 업데이트할 때마다 발생하는 프로브. 인자는 새로운 상태 문자열 |
checkpoint-start | (int) | 체크포인트가 시작되면 발생하는 프로브. 인자는 shutdown나 immediate, force같은 체크포인트 타입을 표시하는 비트 플래그 |
checkpoint-done | (int, int, int, int, int) | 체크포인트가 완료되면 발생하는 프로브(체크포인트 처리 도중에 그 다음 차례인 프로브가 연속적으로 발생함) 첫 번째 인자는 쓰여진 버퍼의 개수. 두 번째 인자는 버퍼의 총 개수. 세 번째, 네 번째, 다섯 번째 인자는 추가되고 삭제되고 재사용된 각 xlog 파일 개수 |
clog-checkpoint-start | (bool) | 체크포인트의 CLOG 부분이 시작됐을 때 발생하는 프로브. 인자를 일반 체크 포인트의 경우 true로, shutdown 체크포인트의 경우 false로 전달 |
clog-checkpoint-done | (bool) | 체크 포인트의 CLOG 부분이 완료되면 발생하는 프로브. 인자는 clog-checkpoint-start인자와 같은 뜻 |
subtrans-checkpoint-start | (bool) | 체크포인트의 SUBTRANS 부분이 시작되면 발생하는 프로브. 인자는 일반 체크포인트의 경우 true로, shutdown 체크포인트의 경우 false로 전달 |
subtrans-checkpoint-done | (bool) | 체크포인트의 SUBTRANS 부분이 끝나면 발생하는 프로브. 인자는 subtrans-checkpoint-start인자와 같은 뜻 |
multixact-checkpoint-start | (bool) | 체크포인트의 MultiXact 부분이 시작되면 발생하는 프로브. 인자는 일반적인 체크포인트의 경우 true로, shutdown 체크포인트의 경우 false로 전달 |
multixact-checkpoint-done | (bool) | 체크포인트의 MultiXact부분이 완료되면 발생하는 프로브. 인자는 multixact-checkpoint-start인자와 같은 뜻 |
buffer-checkpoint-start | (int) | 체크포인트의 버퍼 쓰기 부분이 시작되면 발생하는 프로브. 인자는 shutdown, immediate, force같은 체크포인트 타입을 구별하기 위한 비트 플래그 |
buffer-sync-start | (int, int) | 체크포인트 중간에(어떤 버퍼가 쓰여야 하는지 식별한 후) dirty 버퍼를 쓰기 시작할 때 발생하는 프로브. 첫 번째 인자는 버퍼 총 개수를 뜻함. 두 번째 인자는 현재 dirty하고 쓰여져야 하는 버퍼의 개수를 뜻함 |
buffer-sync-written | (int) | 체크포인트 중간에 버퍼가 쓰여질 때마다 발생하는 프로브. 인자는 버퍼의 ID 넘버 |
buffer-sync-done | (int, int, int) | 모든 dirty 버퍼가 쓰여졌을 때 발생하는 프로브. 첫 번째 인자는 버퍼의 총 개수를 의미. 두 번째 인자는 체크포인트 프로세스가 실제로 쓴 버퍼의 개수. 세 번째 인자는 쓰기로 예상된 버퍼의 넘버(buffer-sync-start의 두 번째 인자). any difference reflects other processes flushing buffers during the checkpoint. |
buffer-checkpoint-sync-start | () | dirty 버퍼가 커널에 쓰여진 이후와 fsync 요청을 보내기 이전에 발생한 프로브 |
buffer-checkpoint-done | () | 버퍼와 디스크 동기화가 완료될 때 발생하는 프로브 |
twophase-checkpoint-start | () | 체크포인트 두 단계가 시작됐을 때 발생하는 프로브 |
twophase-checkpoint-done | () | 체크포인트의 두 단계 부분이 완료됐을 때 발생하는 프로브 |
buffer-read-start | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool) | 버퍼 읽기가 시작될 때 발생하는 프로브. 첫 번째 인자와 두 번째 인자는 페이지의 포크와 블록 넘버를 전달(두 번째 인자는 릴레이션 익스텐션relation extension 요청일 경우 -1). 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스와 데이터베이스, 릴레이션의 식별자인 릴레이션 OID를 뜻함. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. 일곱 번째 인자는 릴레이션 익스텐션을 요청할 때 true이고, 일반 읽기 작업에는 false를 전달함. |
buffer-read-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool, bool) | 버퍼 읽기를 완료했을 때 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크와 블록 넘버를 전달(릴레이션 익스텐션 요청의 경우 두 번째 인자는 새로 추가된 블록의 블록 넘버를 가짐). 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 뜻함. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. 일곱 번째 인자는 릴레이션 익스텐션을 요청할 때 true이고, 일반 읽기 작업에는 false를 전달함. 여덟 번째 인자는 풀에서 버퍼를 발견하면 true이고 아니면 false를 전달함. |
buffer-flush-start | (ForkNumber, BlockNumber, Oid, Oid, Oid) | 공유 버퍼에 쓰기 요청을 하기 전에 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 전달함 |
buffer-flush-done | (ForkNumber, BlockNumber, Oid, Oid, Oid) | 쓰기 요청이 완료됐을 때 발생하는 프로브. (데이터를 커널에 전달하는 시간만 알려 주며, 디스크에 쓰여지지 않았을 때임) 인자들은 buffer-flush-start와 같음 |
buffer-write-dirty-start | (ForkNumber, BlockNumber, Oid, Oid, Oid) | 서버 프로세스가 dirty 버퍼를 쓸 때 발생하는 프로브. (자주 발생하면 shared_buffers가 너무 작거나 bgwriter control 매개변수가 조정이 필요함을 뜻함) 첫 번째와 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달함. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션 식별하는 릴레이션 OID를 전달함 |
buffer-write-dirty-done | (ForkNumber, BlockNumber, Oid, Oid, Oid) | dirty-buffer 쓰기가 완료됐을 때 발생하는 프로브. 인자는 buffer-write-dirty-start와 같음 |
wal-buffer-write-dirty-start | () | WAL 버퍼 공간이 남아 있지 않아서 서버 프로세스가 dirty WAL 버퍼에 쓰기 시작할 때 발생하는 프로브(자주 발생하면 wal-buffers가 너무 작다는 뜻임) |
wal-buffer-write-dirty-done | () | dirty WAL 버퍼 쓰기가 완료됐을 때 발생하는 프로브 |
xlog-insert | (unsigned char, unsigned char) | WAL 레코드가 삽입됐을 때 발생하는 프로브. 첫 번째 인자는 해당 레코드의 리소스 관리자(rmid)임. 두 번째 인자는 info 플래그를 전달함 |
xlog-switch | () | WAL 세그먼트 스위치를 요청했을 때 발생하는 프로브 |
smgr-md-read-start | (ForkNumber, BlockNumber, Oid, Oid, Oid, int) | 릴레이션에서 블록을 읽기 시작할 때 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달함. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 전달. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. |
smgr-md-read-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) | 블록 읽기가 완료됐을 때 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달함. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 전달. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. 일곱 번째 인자는 실제로 읽힌 바이트 수를 뜻하고, 여덟 번째 인자는 요청된 숫자를 뜻함(이 두 개가 다르면 문제가 발생) |
smgr-md-write-start | (ForkNumber, BlockNumber, Oid, Oid, Oid, int) | 릴레이션에 블록을 쓸 때 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달함. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 전달. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. |
smgr-md-write-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) | 블록 쓰기가 완료됐을 때 발생하는 프로브. 첫 번째 및 두 번째 인자는 페이지의 포크 및 블록 넘버를 전달함. 세 번째, 네 번째, 다섯 번째 인자는 테이블스페이스, 데이터베이스, 릴레이션을 식별하는 릴레이션 OID를 전달. 여섯 번째 인자는 로컬 버퍼에 임시 릴레이션을 만든 백엔드의 ID 이거나 공유 버퍼의 InvalidBackendID (-1)을 뜻함. 일곱 번째 인자는 실제로 쓰여진 바이트 수를 뜻하고 여덟 번째 인자는 요청된 숫자를 뜻함(이 두 개가 다르면 문제가 발생) |
sort-start | (int, bool, int, int, bool) | 정렬 작업이 시작됐을 때 발생하는 프로브. 첫 번째 인자는 heap, 인덱스, 데이터 정렬을 뜻함. 두 번째 인자는 unique-value를 시행할 경우 true를 전달. 세 번째 인자는 키 컬럼의 수이고, 네 번째 인자는 허용된 워크 메모리의 킬로바이트 수를 뜻하고, 다섯 번째 인자는 정렬 결과에 랜덤 액세스random access가 요청됐을 때 true를 전달함. |
sort-done | (bool, long) | 정렬이 완료됐을 때 발생하는 프로브. 첫 번째 인자는 외부 정렬일 경우 true이고, 내부 정렬일 경우에는 false임. 두 번째 인자는 외부 정렬에 사용된 디스크 블록의 수이거나 내부 정렬에 사용된 메모리의 킬로바이트 수를 뜻함 |
lwlock-acquire | (LWLockId, LWLockMode) | LWLock이 획득됐을 때 발생하는 프로브. 첫 번째 인자는 LWLock의 ID. 두 번째 인자는 요청된 잠금 모드로 독점적이거나 공유됨 |
lwlock-release | (LWLockId) | LWLock이 풀렸을 때 발생하는 프로브(waiter가 아직 깨어있지 않을 때). 인자는 LWLock의 ID임. |
lwlock-wait-start | (LWLockId, LWLockMode) | LWLock이 즉시 가용하지 않고, 서버 프로세스가 잠금이 가용해질 때까지 기다리기 시작했을 때 발생하는 프로브. 첫 번째 인자는 LWLock의 ID이고, 두 번째 인자는 요청된 잠금 모드로 독점적이거나 공유됨 |
lwlock-wait-done | (LWLockId, LWLockMode) | 서버 프로세스가 LWLock 대기 상태에서 풀려났을 때(잠금을 아직 획득하지 않은 상태) 발생하는 프로브. 첫 번째 인자는 LWLock의 ID임. 두 번째 인자는 요청된 잠금 모드로 독점적이거나 공유됨 |
lwlock-condacquire | (LWLockId, LWLockMode) | 호출자가 대기 하지 않겠다고 하고, LWLock을 획득했을 때 발생하는 프로브. 첫 번째 인자는 LWLock의 ID임. 두 번째 인자는 요청된 잠금 모드로 독점적이거나 공유됨 |
lwlock-condacquire-fail | (LWLockId, LWLockMode) | 호출자가 대기 하지 않겠다고 했는데, LWLock을 획득하지 못했을 때 발생하는 프로브. 첫 번째 인자는 LWLock의 ID임. 두 번째 인자는 요청된 잠금 모드로 독점적이거나 공유됨 |
lock-wait-start | (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) | 잠금이 가용하지 않아서 heavyweight 잠금(lmgr lock) 요청이 대기하기 시작했을 때 발생하는 프로브. 첫 번째부터 네 번째까지의 인자들은 오브젝트가 잠겼는지 식별하는 태그 필드들임. 다섯 번째 인자는 잠긴 오브젝트 타입을 가리키고, 여섯 번째 인자는 요청된 잠금 타입을 가리킴. |
lock-wait-done | (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) | heavyweight 잠금(lmgr lock) 요청의 대기 상태가 끝났을 때(예를 들어 잠금을 획득한 경우) 발생하는 프로브. 인자들은 lock-wait-start의 인자와 같음 |
deadlock-found | () | 교착상태 감지detector가 교착상태를 발견했을 때 발생하는 프로브 |
아래 예시는 트랜잭션 수를 분석하는 DTrace 스크립트로써 성능 테스트 이전과 이후에 pg_stat_database의 스냅샷을 찍는 역할을 한다.
#!/usr/sbin/dtrace -qs postgresql$1:::transaction-start { @start["Start"] = count(); self->ts = timestamp; } postgresql$1:::transaction-abort { @abort["Abort"] = count(); } postgresql$1:::transaction-commit /self->ts/ { @commit["Commit"] = count(); @time["Total time (ns)"] = sum(timestamp - self->ts); self->ts=0; }
수행 하면 위 D 스크립트는 다음과 같이 출력한다.
# ./txn_count.d `pgrep -n postgres` or ./txn_count.d <PID> ^C Start 71 Commit 70 Total time (ns) 2312105013
참고: SystemTap은 trace point가 호환돼도 DTrace와 다른 trace 스크립트 기호를 사용한다. SystemTap 스크립트는 하이픈 기호 대신 이중 언더스코어 기호를 사용해서 프로브 이름을 레퍼런싱해야 한다. 이는 향후 SystemTap 버전에서 개선될 예정이다.
DTrace 스크립트는 쓰거나 디버깅할 때 주의해야 한다. 수집된 trace 정보의 의미가 사라질 수 있기 때문이다. 문제가 발생하는 대부분의 경우, 시스템 내부가 아니라 장치가 원인이다. Dynamic tracing을 사용해서 정보를 얻을 때, DTrace 스크립트도 체크하고 확인해야 한다.
개발자가 원하는 위치 내에서 새로운 프로브를 정의할 수 있는데, 재컴파일이 필요하다. 아래는 새로운 프로브를 삽입하는 과정이다.
1.프로브 이름과 프로브로 가용한 데이터를 정하라.
2.src/backend/utils/probes.d에 프로브 정의를 추가하라.
3.프로브 point가 있는 모듈에 pg_trace.h가 없으면 포함시키고, TRACE_POSTGRESQL프로브 매크로를 소스 코드의 원하는 위치에 넣는다.
4.다시 컴파일하고 새 프로브가 가용한지 확인한다.
Example: 트랜잭션 ID로 모든 새로운 트랜잭션을 추적하기 위해 프로브를 추가하는 예제이다.
1.이름은 transaction-start 이고 LocalTransactionId 타입인 매개변수를 갖는 프로브를 정하라.
2.프로브 정의를 src/backend/utils/probes.d에 추가하라:
probe transaction__start(LocalTransactionId);
프로브 이름 안의 이중 언더스코어가 있음을 유의하자. 프로브를 사용한 DTrace 스크립트에서 이중 밑줄은 하이픈으로 대체되므로 사용자 문서 이름은 transaction-start다.
3.컴파일 시점에, transaction__start는 TRACE_POSTGRESQL_TRANSACTION_START라 불리는 매크로로 바뀐다(언더스코어가 각각 한 개씩 쓰이는 것을 주의하자). 이 매크로는 pg_trace.h를 포함했을 때 가용하다. 매크로 호출 부분을 소스 코드의 적당한 위치에 넣는다. 예를 들면 다음과 같다.
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
4.재컴파일링과 새 바이너리 파일을 실행하면, 아래 DTrace 명령어를 실행했을 때 새로 추가된 프로브가 가용하다는 것을 알 수 있다. 아래와 비슷한 출력을 확인할 수 있을 것이다.
# dtrace -ln transaction-start ID PROVIDER MODULE FUNCTION NAME 18705 postgresql49878 postgres StartTransactionCommand transaction-start 18755 postgresql49877 postgres StartTransactionCommand transaction-start 18805 postgresql49876 postgres StartTransactionCommand transaction-start 18855 postgresql49875 postgres StartTransactionCommand transaction-start 18986 postgresql49873 postgres StartTransactionCommand transaction-start
trace 매크로를 C코드에 추가할 때 몇 가지 주의할 점이 있다.
프로브의 매개변수로 명시된 데이터 타입이 매크로 변수의 데이터 타입과 일치해야 한다. 그렇지 않으면 컴파일 에러가 발생할 것이다.
대부분의 플랫폼의 경우, PostgreSQL이 --enable-dtrace로 설치되면, trace 매크로 인자들을 컨트롤할 때마다 trace하지 않아도 검증된다. 몇 개의 로컬 변수 값을 확인하는 목적이라면 크게 신경 쓰지 않아도 된다. 하지만 인자에 고비용의 함수 호출을 넣는 것은 주의해야 한다. 그런 경우에는 trace가 실제로 활성화 되었는지 확인해서 매크로를 보호하는 것이 좋다. :
if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
각 trace 매크로에는 ENABLED 매크로가 있다.