PostgreSQL 부가 확장 모듈 소개

0. 소개

PostgreSQL에서는 서버 기능을 확장하는 수 많은 부가 확장 모듈을 제공하고 있습니다. 데이터베이스 서버가 이런 일을 할 수 있을까? 생각한다면, 먼저 

http://postgresql.kr/docs/current/contrib.html

페이지를 살펴보길 권장합니다. 해당 페이지에서 각 부가 확장 모듈에 대한 자세한 정보를 담고 있습니다. 

이 글에서는 그 페이지에서 소개하고 있는 모듈들 가운데 실무에서 자주 쓰는, 사용하면 편한 모듈들을 예제와 함께 소개하고자 합니다.


1. adminpack

pgAdmin 프로그램의 서버 관리 항목에서 내부적으로 사용하는 각종 함수들의 모음입니다. pgAdmin 프로그램을 이용해서 데이터베이스 서버 로그를 살펴보고 관리하려고 할 때, 이 확장 모듈을  설치해야합니다. 물론 설치하지 않고, 해당 데이터베이스에 접속하면 pgAdmin에서 친절하게 이 adminpack 모듈을 설치하라고 안내를 해 줍니다. 이 모듈은 각 데이터베이스 별로 설치 되어야 합니다. 
물론 psql에서도 해당 모듈에 포함된 함수를 직접 사용할 수 있습니다. 
보안 문제로 당연히 슈퍼유저 권한으로 접속했을 경우만 함수를 사용할 수 있습니다.

2. auto_explain

PostgreSQL에서는 기본적으로 Dynamic Runtime Instrumentation Tools Architecture (DRITA)에 대한 손 쉬운 함수들을 제공하고 있지 않습니다. PostgreSQL에서는 이 부분에 대한 자료 수집 부분을 이 모듈로 제공합니다. 그 자료는 서버 로그 파일에 저장 됩니다. 이렇게 저장된 정보를 가지고 분석하는 것은 관리자가 직접 처리해야 합니다. 

이 모듈은 사용자 정의 함수가 없습니다. 모든 작업은 서버 환경 변수 값을 조절하는 것 뿐입니다. 물론 이 작업도 모두 슈퍼유저 권한으로만 가능합니다. 

먼저 postgresql.conf 파일을 다음과 같이 바꾸어, 서버가 재실행 될 때 해당 모듈을 미리 로딩하도록 하고, 
shared_preload_libraries = '$libdir/auto_explain'
서버가 재실행 되면, 해당 데이터베이스에 관리자 권한으로 접속해서, 
다음 명령을 실행합니다.
-- 이 모듈을 쓰겠다
postgres=# set auto_explain.log_analyze = on;
SET
-- 모든 쿼리를 대상으로
postgres=# set auto_explain.log_min_duration = 0; 
SET
-- 최대한 자세히
postgres=# set auto_explain.log_verbose = on; 
SET
-- 버퍼 사용량도 함께
postgres=# set auto_explain.log_buffers = on; 
SET
-- 내부적으로 실행되는 쿼리도 함께
postgres=# set auto_explain.log_nested_statements = on; 
SET
이 set 작업은 모두 세션 단위 설정입니다. 
데이터베이스 단위, 또는 서버 단위로 확장하려면, 알맞게 설정하셔야 합니다. 
(서버 환경 설정값 변경 하기에 대한 자세한 이야기는 다음 시간 날 때 작성해야겠네요.)
이 설정이 끝나면, 해당 세션에서 실행되는 모든 쿼리에 대한 아주 자세한 실행 계획이 서버 로그 파일에 기록됩니다.

통상 이 모듈은 운영 전 쿼리 최적화 작업에서 사용하면 좋습니다. 
또한 운영 환경에서는 실행 시간이 느린 쿼리를 대상으로 (auto_explain.log_min_duration 조정) 그 쿼리의 실행 계획 자료를 수집하는 용도로 유용하게 쓰입니다. 

물론 이 기능은 당연히 그 만큼의 서버 부가 비용을 초래하기는 합니다. 이 부분에 대해서는 관리자가 잘 판단해서, 일정 기간만 수집하는 방식으로 처리하면 되겠지요. 

아울러, 지난 게시글에서 서버 로그를 csv 양식으로 저장하는 것에 대한 아이디어를 언급했었는데(링크), 그것과 함께, 이 실행계획 정보를  json 양식으로 저장한다면 (auto_explain.log_format 설정값으로 지정), 완벽하게 컴퓨터가 해석할 수 있는 자료로 처리할 수 있으며, 그것도 실시간으로 처리할 수 있을 것입니다.

이 모듈 사용에서 조심해야 할 부분은 auto_explain.log_timing 설정값은 바꾸지 않는 것이 좋습니다. (리눅스 환경에서 별로 도움이 되질 않습니다. 적어도 제가 사용했던, 사용한 환경에서는)

3. dblink, postgres_fdw

PostgreSQL 서버는 하나의 데이터베이스에서 같은 서버 안에 있는 다른 데이터베이스 자료를 하나의 접속으로는 접근 할 수 없습니다. 그래서 전통적으로 이 모듈을 사용해서 다른 데이터베이스의 자료를 이용했습니다. 
하지만, 9.3 버전에서 postgres_fdw 모듈을 보다 완성도 높게 제공하고 있기 때문에, 이 모듈을 사용하기 보다는 postgres_fdw 모듈을 이용한 SQL/MED 방식의 자료 조작을 권장합니다. 
그런데, 이 postgres_fdw 모듈을 이용한 외부 데이터베이스 쪽에서 실행되는 쿼리의 최적화 기능이 그리 똑똑하지 못해, dbink 모듈을 이용해야할 이유는 충분히 있습니다. 

실무환경에서 최적의 방법은 스스로 결정해야 할 것 같습니다. 

이 두 모듈의 사용 예제는 공식 문서를 참고해도 좋을 만큼 충분히 잘 설명하고 있어 여기서는 생략합니다. 


4. earthdistance

PostgreSQL의 재미난 모듈입니다. 우리말로 옮기면 '지구 거리' 지구본 상의 두 지점간 직선(엄격히 말하면 하나의 호가 되겠지요) 거리를 구하는 함수들의 모음입니다.  이 모듈은 cube 모듈이 필요합니다. 지구본에서의 한 지점은 x,y가 아니라, x,y,z 값으로 표현될터이니 말입니다. (cube 모듈이 이 3차원 자료형을 제공합니다.)

다음은 재미로 찾아본 서울 시청과 부산 시청 사이의 직선 거리입니다.

postgres=# select
earth_distance(ll_to_earth(37.5668689,126.9777054), ll_to_earth(35.179555,129.075641));
earth_distance
-----------------
 325531.79010764
(1 row)

약 326km 라네요.

통상 이 모듈에서 사용하는 계산은 위, 경도 좌표와 지구가 정확한 구(球)라는 아주 이상적인 계산으로 처리됩니다. 실세계는 당연히 다르겠지요. 

이 모듈은 PostGIS 확장 모듈을 사용하지 않고 간단하게, 인근 지하철역 찾기를 GiST 인덱스를 이용해서 찾으려고 할 때 유용하게 사용되는 모듈입니다. 
다음 예제는 인근 도서관 찾기로 대신합니다. 
(지하철역 위, 경도 정보를 제 능력으로는 못 구하겠네요.)

서울시 구립 도서관 정보(wifizone.csv)를 다음 웹페이에서 구했습니다.
http://visualize.tistory.com/57

postgres=# create table libraries (
GU_NM text,FLY_GBN text,LON float,LAT float);
CREATE
-- 데이터 입력
postgres=# \copy libraries from '/tmp/wifizone.csv' (format 'csv', HEADER true);
-- gist 인덱스를 위한 위치 정보 칼럼 추가
postgres=# alter table libraries add p earth;
ALTER TABLE
-- 인덱스 만들기
postgres=# create index libraries_p_i on libraries using gist (p);
CREATE INDEX
-- 해당 칼럼 자료 입력 (이제 위, 경도 칼럼은 없어도 되겠죠)
postgres=# update libraries set p = ll_to_earth(lat, lon);
UPDATE 123
-- 시퀀스 스캔 안함
postgres=# set enable_seqscan = off;
SET
postgres=# explain SELECT * FROM libraries 
postgres-# WHERE p <@ earth_box(ll_to_earth(37.6796272,127.0456284) , 1000);
                                QUERY PLAN                                         
----------------------------------------------------------------
 Index Scan using libraries_p_i on libraries  (cost=0.14..8.15 rows=1 width=98)
   Index Cond: ((p)::cube <@ '(-3042137.60175663, 4028046.09801202, 3897627.5358785),
(-3040137.60175868, 4030046.09800997, 3899627.53587645)'::cube)
(2 rows)

postgres=# SELECT gu_nm, fly_gbn FROM libraries
WHERE p <@ earth_box(ll_to_earth(37.6796272,127.0456284) , 1000);
 gu_nm  |  fly_gbn
--------+------------
 도봉구 | 구립도서관
(1 row)

데이터가 너무 적어서 기본적으로 인덱스를 사용하지 않아, 시퀀스 스캔을 끄고 실행계획을 살펴봤습니다. 
윗 쿼리 결과는 한 지점(37.6796272,127.0456284)에서 반경 1km 내 도서관을 검색한 쿼리입니다.

물론 이 정보는 지극히 수학적인 계산 결과일 뿐입니다. 
현실 세계에서는 이 보다 훨씬 사람 냄세 나는 계산법이 도입되어야겠지요.


5. file_fdw, postgres_fdw

dblink 모듈에서 언급한 외부 데이터 접근에 대한 부분인데, 이 부분은 fdw 관련 글을 준비해서 그곳에서 보다 자세히 다루겠습니다. 유용하게 쓰이는 모듈입니다.

6. pageinspect, pg_buffercache, pg_freespacemap, pgstattuple

이 모듈들은 데이터베이스 관리자가 서버 엔진 부분을 살펴 보려고 할 때, 유용하게 쓰이는 모듈입니다. 

간단하게, 
pageinspect : 데이터 파일 페이지(물리적인 자료 저장 기본 단위)를 쿼리로 살펴볼 수 있는 함수들을 제공합니다.
pg_buffercache : 공유 메모리 영역을 쿼리로 살펴볼 수 있는 함수들을 제공합니다.
pg_freespacemap : 테이블의 재활용 가능한 영역을 살펴볼 수 있는 함수들을 제공합니다.
pgstattuple : 테이블의 물리적인 저장 상태를 살펴볼 수 있는 함수들을 제공합니다.

각 모듈의 자세한 설명은 공식 문서를 참고 하세요. 
이야기가 너무 길어 여기서는 생략합니다. 

7. pg_stat_statements

이 모듈은 서버에서 실행된 쿼리들의 통계 정보를 제공합니다. (오라클의 v$sqlarea 뷰와 비슷합니다.) 
상용 데이터베이스에 비하면 많이 모자라지만, 실험적인 모듈로 때로는(?) 유용하게 쓰입니다. 

8. xml2

이 모듈은 xml 자료형에 대한 xpath 기능을 사용할 수 있도록 함수들을 제공합니다. 
철지난 모듈 같기는 하지만, 아직도 xml 자료는 여전히 유용하게 쓰이고 있으니.

9. 마무리

다 쓰고 보니, 예제를 소개하겠다고 해 놓고선 고작 두 개의 예제만 소개했네요. 
나머지 예제는 각 모듈별 공식 문서 안에서 찾아 보세요. 
여기서 소개한 여러 확장 모듈이 실무 환경에서 얼마나 유용하게 쓰일 지는 모르겠지만, 적어도 PostgreSQL 데이터베이스로 무엇을 할 수 있는가?라는 질문에 어느 정도는 답할 수 있는 소개글이기를 바랍니다.