DB2를 설치하고 WAS 담당자로 부터 JDBC Driver 요청을 받아서 전달해 줄 때 파일명에 4가 붙은 것(db2jcc4.jar)과 안붙은 것(db2jcc.jar)의 차이를 궁금해 했었다.

동기부여가 부족하기도 할 것이고, 업무로 바쁘다 보니 지나치게 되어 버리곤 했는데, 고객사에서 이것에 대해 문의가 왔다. 차이가 뭐냐고…그리고 JDBC 버전 어떻게 확인하냐고…

그래서 DB2에서 제공되는 jdbc driver관련해서 살펴보게 되었다.

DB2 서버에 접근이 가능하다면, DB2 JDBC Driver는 다음과 같은 경로에 존재한다.

(Linux/Unix) DB2설치_경로/java  혹은 instance경로/sqllib/java

(Windows) C:\Program Files\IBM\SQLLIB\java

 

WAS가 DB2 접속에 필요한 파일은 2가지이다.

- db2jcc.jar (혹은 db2jcc4.jar)

- db2jcc_license_cu.jar  (db2jcc_license_cisuz.jar 는 db2 connect 서버에서 제공되는 driver)

 

설치된 DB2 JDBC driver 버전 확인 (java 명령어가 실행되어야 함)

> java com.ibm.db2.jcc.DB2Jcc -version
IBM DB2 JDBC Universal Driver Architecture 3.62.56

 

jdbc driver 파일에 대해서 수행하여 version을 확인할 수도 있다.

java –cp 드라이버파일이름 com.ibm.db2.jcc.DB2Jcc –version

> java -cp db2jcc4.jar com.ibm.db2.jcc.DB2Jcc -version
IBM Data Server Driver for JDBC and SQLJ 4.13.127

db2jcc.jar 와 db2jcc4.jar 2개의 driver 는 DB2 V9.5에서 부터 시작되었다.

http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=%2Fcom.ibm.db2.luw.wn.doc%2Fdoc%2Fc0051316.html

문서를 보면 JDBC 3.0 이하의 기능 호환성을 필요로 할 때는 db2jcc.jar 를

JDBC4.0 이상의 기능 호환성을 필요로 할 때는 db2jcc4.jar를 사용하도록 하였다.

SUN의 JDK 1.6 (혹은 JDK 6) 과 JDK 1.4 버전의 구분에 따라 db2 jdbc driver도 구분을 하게 된 듯 하다. (ORACLE JDK보다는 SUN JDK로 명명하는 것이 친숙하여 SUN JDK로 명명하였다.)

아마 JDK 1.4와 JDK6 에 대한 기능 차이는 자바 개발자가 더 잘 알듯하다.

 

참고로 JDBC 관련 유용 자료를 정리해 소개해 본다.

1. DB2 JDBC Drvier 관련 Developer Work 기술 문서

http://www.ibm.com/developerworks/data/library/techarticle/dm-0512kokkat/

 

2. JDBC Driver 버전 관련 IBM Tech Note

http://www-01.ibm.com/support/docview.wss?uid=swg21363866

(참고) 빌드 번호의 의미

- sYYMMDD 이며, YY는 연도, MM은 월, DD는 일을 의미

 

3. KDUG 에 기재된 JDBC 정리 자료

http://www.kdug.kr/blog/DB2_Tips/329/3?blcode=DB2_Tips&page=3&que=1

국내외적으로 Oracle 사용자가 많은 관계로 (여러 DBMS의 제조사 세미나를 가보면서) Oracle은 DBMS 계의 공공의 적이 되었다는 생각이 들곤 한다. IBM DB2의 경우 Mainframe (z-Series) 분야에서는 평정한 DBMS 이나 많이 사용되는 Unix, Linux, Windows 시장에서는 그렇지 못하기에 DB2 for LUW 제품에는 오라클 SQL을 실행할 수 있는 요건이 필수적이 되었다. 오라클을 사용하는 고객을 대상으로 DB2로 전환하기 위해서는 Oracle 의 기능을 흡수할 필요도 있고 낯선 DB2를 친숙하게 하는 효과를 만들었어야 했을 것이다.

V9.1까지는 java 기반으로 만들어진 udf를 Oracle DB 전환 시 사용하였으나, V9.5부터는 DB2 자체에 변환기를 내포하기 시작했다. V9.7에서는 훨씬 많은 기능들이 내장되어 Oracle DB를 전환하는 경우 많은 수작업들을 안해도 되어 DB2 기술자의 단순 작업 부담이 줄었다 할 수 있겠다.

그러나 이로 인한 단점도 생긴다. (필자의 경우는 오라클 호환성 기능이 생기면서 부정적인 생각을 많이 했다. DB2 자체 기능으로 개발하여 보완하는 식으로 갔으면 좋은데 변환 방식으로 갔기 때문이다..)

앞으로 말하려고 하는 오라클 호환성 rownum 은 테스트를 해본 바, Oracle rownum 보다 더 일관적인 느낌이여서 낫다는 생각을 해본다.

KDUG 등에  오라클과의 rownum 비교 자료를 보곤 했는데, 그것 말고도 고려할 점이 있었다.

 

테이블 DDL Script

오라클

create table t1(c1 integer,c2 integer);
insert into t1 values(1,6);
insert into t1 values(2,5);
insert into t1 values(3,4);
insert into t1 values(4,3);
insert into t1 values(5,2);
insert into t1 values(6,1);
commit;


DB2

db2 "create table t1(c1 integer,c2 integer)"
db2 "insert into t1 values (1,6),(2,5),(3,4),(4,3),(5,2),(6,1)"

 

ORACLE DB에 저장된 데이터

ORACLE> select rownum, t1.* from t1;

    ROWNUM         C1         C2
---------- ---------- ----------
         1          1          6
         2          2          5
         3          3          4
         4          4          3
         5          5          2
         6          6          1

DB2 DB에 저장된 데이터

DB2 > db2 "select rownum, t1.* from t1"

ROWNUM               C1          C2
-------------------- ----------- -----------
                   1           1           6
                   2           2           5
                   3           3           4
                   4           4           3
                   5           5           2
                   6           6           1

 

1. ORDER BY 시의 오라클과 DB2의 차이점

오라클 결과

ORACLE> select rownum, t1.* from t1 order by c2;

    ROWNUM         C1         C2
---------- ---------- ----------
         6          6          1
         5          5          2
         4          4          3
         3          3          4
         2          2          5
         1          1          6

DB2 결과

ROWNUM               C1          C2
-------------------- ----------- -----------
                   1           6           1
                   2           5           2
                   3           4           3
                   4           3           4
                   5           2           5
                   6           1           6

오라클과 DB2의 rownum 결과값이 달라졌다.

이 결과를 보면, Oracle은 Insert 시에 rownum의 값이 만들어져 저장되는 것 처럼 여겨진다. DB2는 “최종 데이터 결과 집합”에 rownum 값을 부여하는 것처럼 여겨진다.

그러면서 의문을 하게 갖게 된다. 테이블 2개를 join하여 rownum 값을 출력시키면 어떻게 될 것인가? DB2는 “최종 데이터 결과 집합”에 부여를 하니 문제없을 것 같은데, Oracle은 rownum 값에 대해 “애매하다” (ambiguous)는 에러를 반환하게 되지 않을까? 하는 예측을 하게 된다.

이 내용관련 정리하는 시점에는 에러가 발생하지 않았지만, 맨 처음 테스트를 했을 때는 오라클에서는 이런 에러 메시지가 났었다.

ORA-01747: 열명을 올바르게 지정해 주십시오

동일 SQL을 DB2와 oracle에 동일하게 수행했으니 신택스 오류는 없었을 것으로 생각되는데, 다시 테스트를 했을 때는 동일한 오류가 발생하지 않으니 신택스 문제처럼 보여지게 된다..

위 메시지가 났을 때 “오라클은 Insert 시에 rownum 값에  많은 영향을 주는 것 처럼” 판단을 했었다.

 

2. JOIN 시의 rownum 결과

오라클

ORACLE> select rownum, t1.c1, t2.c1 from t1, t2 where t1.c1=t2.c1;

    ROWNUM         C1         C1
---------- ---------- ----------
         1          2          2
         2          3          3

DB2

DB2 > db2 "select rownum, t1.c1, t2.c1 from t1, t2 where t1.c1=t2.c1"

ROWNUM               C1          C1
-------------------- ----------- -----------
                   1           2           2
                   2           3           3

위 결과를 보면, 오라클도 DB2로 “최종 데이터 결과 집합”에 rownum을 부여하는 것으로 나온다.

이 결과로 오라클은 rownum 처리 방식을 약간 혼란스럽게 만드는 반면, DB2는 예측되는 결과를 가져다 준다. (DB2가 더 일관적인 처리를 해준다라는 생각을 한다.)

이런 결과들을 통해서 group by 절과 조건 절에 rownum을 추가하면 어떤 결과를 주게 될 것인가?를 예측해 본다면 “최종 결과 집합”에 대해 rownum 으로 값이 짤리겠지하는 예측을 하게 될 것이다.

 

3. GROUP BY 및 검색 조건의 ROWNUM

오라클

create table t1 (c1 integer, c2 varchar(3));
insert into t1 values (1,'a');
insert into t1 values (2,'a');
insert into t1 values (3,'b');

create table t2 (c1 integer, c2 varchar(3));
insert into t2 values (1,'a');
insert into t2 values (2,'b');

create table t3 (c1 integer, c2 integer);
insert into t3 values (1,1);
insert into t3 values (1,2);
insert into t3 values (1,3);
insert into t3 values (2,1);
insert into t3 values (2,2);

DB2

db2 "create table t1 (c1 integer, c2 varchar(3))"
db2 "create table t2 (c1 integer, c2 varchar(3))"
db2 "create table t3 (c1 integer, c2 integer)"
db2 "insert into t1 values (1,'a'),(2,'a'),(3,'b')"
db2 "insert into t2 values (1,'a'),(2,'b')"
db2 "insert into t3 values (1,1),(1,2),(1,3),(2,1),(2,2)"

오라클 결과

ORACLE> select t1.c1, count(t1.c2),count(t2.c1) from t1, t2, t3 where t1.c2=t2.c2 and t1.c1=t3.c1 and rownum < 4 group by t1.c1 ;


        C1 COUNT(T1.C2) COUNT(T2.C1)
---------- ------------ ------------
         1            3            3

DB2 결과

DB2> db2 "select t1.c1, count(t1.c2),count(t2.c1) from t1, t2, t3 where t1.c2=t2.c2 and t1.c1=t3.c1 and rownum < 4 group by t1.c1"

C1          2           3
----------- ----------- -----------
          1           3           3

rownum < 4 이므로 3건의 데이터가 나와야 하는데, 1건만 나온다.

예측한 결과와 다른 반응을 보여준다.

 

그렇다면 group by 만 제외한 결과는 어떻게 나올까?

오라클

ORACLE> select t1.c1, t2.c1, t3.c1 from t1, t2, t3 where t1.c2=t2.c2 and t1.c1=t3.c1 and rownum < 4 ;

        C1         C1         C1
---------- ---------- ----------
         1          1          1
         1          1          1
         1          1          1

 

DB2

DB2> db2 "select t1.c1, t2.c1, t3.c1 from t1, t2, t3 where t1.c2=t2.c2 and t1.c1=t3.c1 and rownum < 4"

C1          C1          C1
----------- ----------- -----------
          1           1           1
          1           1           1
          1           1           1

위 결과를 통해서 “이유”를 알게 되었을 것이다.

rownum 보다 group by가 우선 실행되어지는 것을!!

 

이 문제는 프로젝트 지원하면서 개발자가 페이징처리를 하면서 “왜 rownum으로 짜른 만큼의 데이터 건 수가 나오지 않느냐?” 하는 질문을 하게 되어 살펴 보게 되었다.

이런 현상에 대해 이것 저것 테스트를 하면서 위와 같은 궁금증에 대해 테스트를 해보면서 (SQL을 잘 쓰지 않아서 생기는 문제일 수도 있겠지만) 사용자의 허를 찌르는 부분이 존재하는 것을 깨닫게 되었다.

맨 처음 접했을 때는 SQL의 조그마한 변경에도 일관되지 않는 결과값들이 나와서,,별별 의심을 다 해보게 되고, DB2의 rownum에 무슨 문제가 있는 것은 아닌가 하는 부정적인 시각도 갖게 되었었다.

위 경우 페이징처리는 오라클도, DB2도 subquery (결과 집합을 한번 더 감싸서 짤라내는)로 만들어 처리할 수 밖에는 없다. (페이징처리를 하는 기준 컬럼이 인덱스를 활용할 수 있다면 다른 방법으로 처리할 수도 있겠지만..)

락 상태를 확인하거나 Waiting에 빠진 상태를 확인하기 위해 db2top (U옵션) 혹은 db2pd –locks wait 등의 명령어를 통해 확인을 한다.

두 명령어 모두 서버에서만 실행가능한 명령어이기 때문에 사용의 제약이 발생한다. SQL로 실행하여 lock 상태는 다음과 같이 snapshot 함수를 통해 확인 가능하다.

 

SELECT a.locks_held,

             a.MEMBER,

              a.agent_id app_handl,

              substr(b.appl_name,1,20) app_name,

              a.APPL_CON_TIME,

              a.lock_wait_time,

              substr(b.client_nname,1,20) client_name,

              b.client_platform

FROM SYSIBMADM.SNAPAPPL AS a,

          SYSIBMADM.APPLICATIONS b

WHERE locks_held > 0 AND a.agent_id = b.agent_id

order by locks_held desc

결과 값

LOCKS_HELD  MEMBER  APP_HANDL  APP_NAME  APPL_CON_TIME  LOCK_WAIT_TIME CLIENT_NAME  CLIENT_PLATFORM

------------- ---------- ------------ ----------- ----------------- -------------------- -------------------- ---------------

      2               0             80                db2bp          2012-08-24-12.14.15.363344  0  pureScale                  LINUXX8664

 

App_Handle이 80인 Application이 2개의 lock를 가지고 있음을 확인할 수 있다.

잠금 대기(Lock waiting)에 놓인 테이블을 통하여 어떤 Application에 의해 Lock Chain이 발생했는지 확인 가능하다.

select substr(tabschema,1,8) || '.' || substr(tabname,1,16) tabname,

          lock_name,

          substr(lock_object_type,1,12) lock_object_type,

          req_application_handle,

          req_member,substr(req_application_name,1,14) req_app_name,hld_member,

           hld_application_handle,

           substr(hld_application_name,1,14) hld_app_name,

           substr(req_stmt_text,1,32) req_stmt,

           substr(hld_current_stmt_text,1,26) hel_cur_stmt

from SYSIBMADM.MON_LOCKWAITS

결과 값

TABNAME LOCK_NAME LOCK_OBJECT_TYPE REQ_APPLICATION_HANDLE REQ_MEMBER REQ_APP_NAME HLD_MEMBER HLD_APPLICATION_HANDLE HLD_APP_NAME REQ_STMT HEL_CUR_STMT

--------- -------------------------------- ---------------- ---------------------- ---------- -------------- ---------- ---------------------- -------------- -------------------------------- --------------------------

INST10 .T1 03000800040000000000000052 ROW 70 0 db2bp 0 60 db2bp delete from t1

T1 테이블에 대해서 Lock을 잡고 있는 Application(HLD_APPLICATION_HANDLE) 은 60번이고,

Lock 을 얻고자 요청한 Application(REQ_APPLICATION_HANDLE)은 70번이다.

이 경우 lock chain 현상을 풀고자 60번 Application을 강제로 죽이면 chain현상은 풀리게 된다.

db2 “force application(‘60’)”

DB2에 접속한 Application 확인 방법은 다양하다. db2pd, db2 list application 및 db2 스냅샷 함수 등등…

그런데 Application 접속에 의해 파생된 추가적인 쓰레드 들을 제외한 것을 보고자 하는 요건이 많을 것이다. (순수하게 db에 접속해서 작업을 요청하는 쓰레드만..)

쉽게 접속 수를 확인하는 방법은 db2 list active databases 명령어일 것이다.

실행 예
$>  db2 list active databases

                           Active Databases

Database name                              = SAMPLE
Applications connected currently           = 3
Database path                      = /database/inst10/NODE0000/SQL00001/MEMBER0000/

 

접속된 3개의 Applications에 대해서는 다음 명령어를 통하여 정확히 확인할 수 있다.

$> db2 list applications

Auth Id  Application    Appl.      Application Id                                   DB         # of

            Name           Handle                                                         Name       Agents
------- ----------- -------- ------------------------------------ --------   -----
INST10   db2bp.exe      296     192.168.137.1.62716.12091006351           SAMPLE   1
INST10   db2bp          8          *LOCAL.inst10.120910020559               SAMPLE   1
INST10   javaw.exe      297        192.168.137.1.62718.12091006351        SAMPLE   1

db2 명령어로 된 것은 DB2 관리를 위해 자동화할 때는 문제가 생긴다.

위 명령어와 동일한 결과값은 다음과 같은 SQL로 구현될 수 있다.

select substr(session_auth_id,1,12) auth_id,

          substr(application_name,1,12) app_name,

          application_handle, substr(application_id,1,18) app_id,

           member,

          client_pid,

          substr(client_prdid,1,12) clnt_ver,

          client_platform,

          substr(client_applname,1,10) clnt_appname,

          connection_start_time

from TABLE(mon_get_connection(cast(null as bigint),-2)) as a

실행 결과

AUTH_ID    APP_NAME     APPLICATION_HANDLE   APP_ID   MEMBER CLIENT_PID           CLNT_VER     CLIENT_PLATFORM CLNT_APPNAME CONNECTION_START_TIME    
------------ ------------ -------------------- ------------------ ------ -------------------- ------------ --------------- ------------ --------------------------
INST10       db2bp.exe                     296 192.168.137.1.6271      0                 6372 SQL09074     NT64            CLP C:\Pro   2012-09-10-15.35.15.584196

INST10       db2bp                           8 *LOCAL.inst10.1209      0                 4897 SQL10010     LINUXX8664      -            2012-09-10-11.05.59.404087

INST10       javaw.exe                     297 192.168.137.1.6271      0                 7560 SQL09074     NT64            -            2012-09-10-15.35.15.742185

부가적으로 클라이언트쪽 정보를 더 넣었지만, 이 결과를 보면서 접속된 Application 수와 어떤 프로그램을 통해 접속이 이루어졌는지 확인할 수 있을 것이다.

'Note' 카테고리의 다른 글

[SQL] rownum 사용 시 주의점  (0) 2012.10.22
[관리] Lock 모니터링 및 세션 관리  (0) 2012.09.11
[개발] 오라클 정규식 함수의 이관  (0) 2012.09.10
[SQL] Multi Action Trigger  (0) 2012.05.31
[관리] Offline DB 백업의 복원  (0) 2012.04.23

프로젝트 지원 업무가 있어 ORACLE의 프로시저를 변환 작업 중 정규식 함수로 프로시저 컴파일이 되지 않는 문제가 발생을 했었다.

DB2에서 제공되는 정규식 함수를 찾아 보니 9개 정도가 된다.

- REGR_AVGX, REGR_AVGY, REGR_COUNT, REGR_INTRCEPT, REGR_R2, REGR_SLOPE, REGR_SXX, REGR_SXY, REGR_SYY

통계와 관련된 어려운 함수들만 제공되는 것 같다.

개발자가 문의한 함수는 REGEXP_SUBSTR() 인데 위에 언급했다시피 존재하지 않는다. 웹에서 검색을 해 보니, (V8, V9.1 버전 시절의 오라클 함수 포팅 방법인) java 함수로 해당 함수를 포팅할 수 있도록 IBM Developerworks 기술 문서에 기술되어 있었다.

제공되는 함수는 4가지 (REGEXP_LIKE, REGEXT_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR)…

잘 사용되는 대부분의 오라클 함수는 지원될 것이라 생각했는데, 이렇게 피해가는 함수들이 존재를 한다.

해당 자바 함수를 DB에 생성하기 위해서는 db2_regex.jar 파일이 필요하다. (블로그에 파일이 첨부되지 않으므로, jar 파일은 글 아래의 URL 주소를 통하여 다운로드 받도록 한다.)

해당 JAR 파일은 SQLJ.INSTALL_JAR 프로시저를 이용하여 설치한다.

구문
call sqlj.install_jar(‘file:JAR_파일경로_파일명’, JAR_ID)

실행 예
CALL SQLJ.INSTALL_JAR('file:/tmp/avalanche/db2_regex/lib/db2_regex.jar', db2_regex)

 

생성해야 되는 정규식 함수 script는 다음과 같다.

CREATE OR REPLACE FUNCTION REGEXP_LIKE(SOURCE VARCHAR(3000), REGEX VARCHAR(512), MODE VARCHAR(3))

RETURNS INTEGER
FENCED
NOT DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'db2_regex:com.ibm.avalanche.udf.regex.Regexp.regexpLike'
NO EXTERNAL ACTION
@
 

CREATE OR REPLACE FUNCTION REGEXP_REPLACE(SOURCE VARCHAR(3000), REGEX VARCHAR(512), REPLACEMENT VARCHAR(3000), POSITION INTEGER, OCCURRENCE INTEGER, MODES VARCHAR(3))

RETURNS VARCHAR(3000)
FENCED
NOT DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'db2_regex:com.ibm.avalanche.udf.regex.Regexp!regexpReplace'
NO EXTERNAL ACTION
@
 

CREATE OR REPLACE FUNCTION REGEXP_SUBSTR(SOURCE VARCHAR(3000), REGEX VARCHAR(512), POSITION INTEGER, OCCURRENCE INTEGER, MODES VARCHAR(3))

RETURNS VARCHAR(3000)
FENCED
NOT DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'db2_regex:com.ibm.avalanche.udf.regex.Regexp!regexpSubstr'
NO EXTERNAL ACTION
@
 
CREATE OR REPLACE FUNCTION REGEXP_INSTR(SOURCE VARCHAR(3000), REGEX VARCHAR(512), POSITION INTEGER, OCCURRENCE INTEGER, ROPT INTEGER, MODES VARCHAR(3))

RETURNS INTEGER
FENCED
NOT DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'db2_regex:com.ibm.avalanche.udf.regex.Regexp!regexpInstr'
NO EXTERNAL ACTION
@

jar 설치부터 자바 함수 생성은 하나의 script로 작성하여 생성 작업을 완료할 수 있다.

스크립트 파일 예

vi /work/reg.db2
CALL SQLJ.INSTALL_JAR('file:/work/db2_regex.jar', db2_regex) @

CREATE OR REPLACE FUNCTION REGEXP_LIKE(SOURCE VARCHAR(3000), REGEX VARCHAR(512), MODE VARCHAR(3))

RETURNS INTEGER
FENCED
NOT DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'db2_regex:com.ibm.avalanche.udf.regex.Regexp.regexpLike'
NO EXTERNAL ACTION
@



생성 예

db2 connect to 디비명 user 사용자ID using 사용자비밀번호
db2 –td@ –v –f /work/reg.db2
db2 terminate

 

참고 문서

1. DB2에서 제공하는 정규식 함수

http://pic.dhe.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=%2Fcom.ibm.db2.luw.sql.ref.doc%2Fdoc%2Fr0002321.html

2. 자바 정규식 함수 생성 및 실행

http://www.ibm.com/developerworks/kr/data/library/techarticle/dm-1011db2luwpatternmatch/

일반적인 트리거는 테이블에 이벤트 특정 한 개의 작업 (삭제 혹은 입력, 변경)을 하게 되어 있다.

그러나 9.7 FixPack4 부터는 여러 작업을 수행할 수 있도록 트리거 기능이 개선되었다.

업무에 따라, 회사마다 정책에 의해서 트리거를 쓸 수도 있고, 안쓸수도 있지만 “특정 이벤트에 여러 작업을 해야 되는 요건”이 있다면 multi action trigger를 검토할 필요성이 있을 것 같다.

1번 테이블에 데이터가 입력되거나 변경되는 경우, 2번 테이블에 insert 및 update 작업이 기록되도록 하여 multi action이 적용하는지를 테스트 해 보았다.

테이블 생성

db2 “create table t1 (c1 integer, c2 timestamp)
db2 “create table t2 (c1 varchar(10), c2 timestamp)

 

트리거 생성

db2 +p -td"@" -v << EOF

connect to sample @

CREATE OR REPLACE TRIGGER tr1
  BEFORE INSERT OR UPDATE ON t1
  FOR EACH ROW
begin
    IF (INSERTING) THEN
         insert into t2 values ('inserting',current timestamp);
    END IF;

    IF (UPDATING ) THEN
         insert into t2 values ('updating',current timestamp);
    END IF;
end
@
terminate @
EOF

1번 테이블에 데이터 입력 및 2번 테이블의 insert 실행기록 확인

$ db2 "insert into t1 values (1,current timestamp),(2,current timestamp)"
DB20000I  The SQL command completed successfully.

$ db2 "select * from t2"

C1         C2
---------- --------------------------
inserting  2011-10-19-16.19.27.047307
inserting  2011-10-19-16.19.27.075943

  2 record(s) selected.

 

1번 테이블의 데이터 변경 및 2번 테이블의 update 실행기록 확인

$ db2 "update t1 set c1=11 where c1 =1"
DB20000I  The SQL command completed successfully.

$ db2 "select * from t2"

C1         C2
---------- --------------------------
inserting  2011-10-19-16.19.27.047307
inserting  2011-10-19-16.19.27.075943
updating   2011-10-19-16.20.24.818192

  3 record(s) selected.

Offline Backup은 Database가 메모리에서 내려간 상태에서 받아진 것을 의미한다. 다른 관점에서는 Database에 접속이 이뤄지지 않는, 업무 서비스가 되지 않는 상황에서의 백업을 의미한다.

특정 DBMS에서는 이런 offline backup 개념이 없을 수도 있겠지만, DB2에서 백업은 트랜잭션을 기록하는 로그 관리 방식과 밀접한 관련을 갖는다.

순환로깅 방식을 사용하는 경우 백업은 offline 상태에서 진행되고, 아카이브 로깅 방식(사용한 트랜잭션 로그를 버리지 않고 보관)을 사용하는 경우는 offline, online 백업이 가능해 진다.

대부분의 고객사에서는 서비스가 중지되는 시간을 줄이기 위해 Archive 로깅 모드로 DB를 운영한다. 이런 상황에 offline backup을 못하는 것은 아니지만, 받아 놓은 offline backup을 restore 하다보면 약간 당황스러운 상황이 발생한다.

DB2 걸음마 시절, 고객사에서 갑작스럽게 restore 작업을 하다가 탐탁치않게 보던 시선을 느꼈던 순간이 기억난다..

1. 로깅 방식 확인

   V8 버전 이하의 경우, 혹은 전문 백업 솔루션을 사용하는 경우 좀 더 상세히 살펴봐야 하지만 보통은 아래 변수의 설정값을 통하여 Circular 모드인지 아닌지를 확인할 수 있다.

$ db2 get db cfg for sample | grep -i logarchmeth1

First log archive method                 (LOGARCHMETH1) = OFF
Options for logarchmeth1                  (LOGARCHOPT1) =

LOGARCHMETH1 값이 설정되지 않은 경우 Circular 모드로 운영 중임을 의미한다.

이런 경우는 offline backup 을 restore 하면 바로 db 접속이 가능해 진다.

$ db2 backup db sample to /instance/inst97 compress
Backup successful. The timestamp for this backup image is : 20120423104058

$ db2 drop db sample
DB20000I  The DROP DATABASE command completed successfully.

$ db2 restore db sample from /instance/inst97
DB20000I  The RESTORE DATABASE command completed successfully.

$ db2 connect to sample

  Database Connection Information

Database server        = DB2/LINUXX8664 9.7.4
SQL authorization ID   = INST97
Local database alias   = SAMPLE

그러나 아카이빙 로그 모드로 운영되는 경우 offline backup 이미지를 복원하는 경우 바로 접속되지 않는다.

$ db2 get db cfg for sample | grep -i logarchmeth1

First log archive method                 (LOGARCHMETH1) = DISK:/instance/inst97/archive/
Options for logarchmeth1                  (LOGARCHOPT1) =

$ db2 backup db sample to ~/archive compress

Backup successful. The timestamp for this backup image is : 20120423104901

$ db2 drop db sample

DB20000I  The DROP DATABASE command completed successfully.

$ db2 restore db sample from ~/archive

DB20000I  The RESTORE DATABASE command completed successfully.

$ db2 connect to sample

SQL1117N  A connection to or activation of database "SAMPLE" cannot be made
because of ROLL-FORWARD PENDING.  SQLSTATE=57019

위와 같이 SQL1117N 코드를 반환한다. 보통은 online backup을 복원 후, rollforward 작업을 하지 않는 경우에 발생하는 메시지이다.

위의 문제 해결을 2가지 방법이 존재한다.

$ db2 restore db sample from ~/archive

$ db2 rollforward db sample query status

                                Rollforward Status

Input database alias                   = sample
Number of nodes have returned status   = 1

Node number                            = 0
Rollforward status                     = DB  pending
Next log file to be read               = S0000001.LOG
Log files processed                    =  -
Last committed transaction             = 2012-04-23-02.09.42.000000 UTC



$ db2 rollforward db sample stop

                                 Rollforward Status

Input database alias                   = sample
Number of nodes have returned status   = 1

Node number                            = 0
Rollforward status                     = not pending
Next log file to be read               =
Log files processed                    =  -
Last committed transaction             = 2012-04-23-02.09.42.000000 UTC

DB20000I  The ROLLFORWARD command completed successfully.

혹은 다음과 같이 할 수도 있겠다.

$ db2 restore db sample from ~/archive without rolling forward
DB20000I  The RESTORE DATABASE command completed successfully.

$ db2 rollforward db sample query status


                                Rollforward Status

Input database alias                   = sample
Number of nodes have returned status   = 1

Node number                            = 0
Rollforward status                     = not pending
Next log file to be read               =
Log files processed                    =  -
Last committed transaction             = 2012-04-23-02.09.42.000000 UTC

 

만일 online backup 을 restore 하고 위와 같이 rollforward를 중지하도록 하는 경우, rollforward pending은 풀리지 않는다.

$ db2 rollforward db sample stop

SQL1276N  Database "SAMPLE" cannot be brought out of rollforward pending state
until roll-forward has passed a point in time greater than or equal to
"2012-04-23-02.20.40.000000 UTC", because node "0" contains information later
than the specified time

$ db2 rollforward db sample query status


                                Rollforward Status

Input database alias                   = sample
Number of nodes have returned status   = 1

Node number                            = 0
Rollforward status                     = DB  working
Next log file to be read               = S0000000.LOG
Log files processed                    =  -
Last committed transaction             = 2012-04-23-02.20.40.000000 UTC

$ db2 connect to sample

SQL1117N  A connection to or activation of database "SAMPLE" cannot be made
because of ROLL-FORWARD PENDING.  SQLSTATE=57019

온라인 백업은 백업 이미지에 포함된 archive log 를 추출하여 최소한의 시점 복구(rollforward)를 해 주어야 한다.

IBM에서 타 제조사의 DBMS를 DB2로 변경하는 경우, 관련 작업들을 편하고 자동화할 수 있도록  Tool을 제공한다. 한 때는 MTK(Migration Toolkit) 이라는 것을 제공했으나 현재는 V9.7과 같이 나온 DMT(Data Movement Tool) 이라는 것을 제공하고 있다.

어떤 면에서는 MTK가 좋은데, 또 어떤 면에서는 DMT가 낫다.

1. 비교

항목

MTK

DMT

DB연결방식 ODBC JDBC Type 4
운영체제 Windows, Linux, Unix Windows, Linux, Unix
원본 DBMS Oracle, MSSQL, Sybase ASE, MySQL Oracle,MSSQL, Sybase, Access, MySQL, Postgres, db2, db2 /z
목표 DBMS DB2, Informix DB2
장점 1.GUI 화면에서 선택적 추출
2.파일 대상으로 변환 가능
3.DBMS 고유 함수, 프로시저들을 java routine으로 제공
4.다양한 포맷방식으로 data 추출
1. DB접속을 위한 db client 불필요
2. object 와 제약조건이 분리되어 추출
3. 데이터 분할하여 추출
4. 데이터 추출, 적재작업의 병렬처리
5. 데이터 검증 등의 script 제공
단점 1.object DDL 문과 제약조건이 섞여서 추출됨
2. 추출/변환 시 사용자 요건에 맞는 변경이 제한적임
1. DB를 대상으로 추출/변환
2. MTK에 비해 GUI 기능이 약함
3. Oracle 과 DB2 V9.7에 최적화됨

 

2. MTK 화면

제품 다운로드: http://www.ibm.com/developerworks/data/downloads/migration/mtk/

 

3. DMT 화면

제품 다운로드: http://www.ibm.com/developerworks/data/library/techarticle/dm-0906datamovement/

 

자세한 사용법은 위 URL을 통하여 확인하고 다운받아 사용해보면 될 것 같다.

원본 서버에 접속할 수 없는 환경이여서 고객쪽에서 text 파일로 ddl문을 제공하는 경우는 MTK를 적용하여 이관 작업을 진행할 수 밖에 없을 것이다.

반면 테이블의 데이터가 대용량인 경우, MTK가 실행되는 file system 공간이 여유가 없다면 DMT를 이용하여 데이터를 나눠 추출하여 대용량 데이터를 이관해야되는 상황도 있을 것이다. 또는 원본 DBMS의 클라이언트를 다운받아 설치할 수 없어, 접속이 힘든 경우에도 DMT를 사용해야 될 것이다.

지금 시점에는 V9.7에 최적화된 DMT를 사용하는 것이 일반적이겠지만, DMT 사용을 할 수 없는 경우엔 MTK를 사용을 고려해 봐야 될 듯 하다.

시스템을 관리하다가 오류가 발생해서, 아니면 갑작스럽게 DB 시스템이 느려지거나 heavy한 SQL이 있나 해서 수행 중인 SQL모을 확인해 보고자 하는 경우가 있을 것이다.

IBM에서 무료로 제공하는 모니터링 도구 중 잘 사용되는 것이 db2pd와 db2top 2가지가 있다.

db2pd는 informix의 onstat 의 메커니즘을 적용한 도구로 DB2가 사용하는 메모리에서 정보를 수집하게 해주는 도구이다. 장점은 DB 접속 절차가 없어서 db가 hang이 발생하는 순간에도 DB의 정보 수집이 가능하다는 점일 것이다. 단점은 많은 정보가 추출되고, Text 기반으로 출력 형식이 정해져 있어서 보고자 하는 정보를 찾거나 보기가 불편하다는 점일 것이다.

반면, db2top은 (v9.5부터 제품 설치 시 제공되어짐, 하위 버전의 경우는 최신 fixpack에 포함되어제공됨)  snapshot 함수를 이용한 방식으로 telnet 등의 서버 레벨에서 정보를 보기 편하게 제공한다. (linux의 top이나 aix의 topas 같은 Text 기반 GUI ?)

단점은 DB 서버에 부하가 심하거나 하는 경우 수집이 잘 되어지지 않는다. DB에  접속을 해서 정보를 수집하기에 “성능 부하” 문제로 정보를 모니터링 하기에는 조금 힘든 면이 있다. Text 기반으로 정보를 보여주어도 보기 편하게 제공을 하기 때문에 편의성은 좋다고 할 수 있겠다.

이 중 db2top을 이용하여 실행 중인 SQL을 확인해 보는 방법을 살펴보고자 한다.

DB2 서버에 telnet 으로 접속(db2 사용자 계정) 을 하여 수행한다. (DB명: sample)

명령어
> db2top –d sample

대문자 D키를 누르고,

대문자 L 키를 눌러 보고자 하는 SQL의 Hash Value 입력

실행 중인 SQL 전체 구문을 얻을 수 있다. Dynamic SQL 경우에는 (jsp 등에서 ? 처리해서 동적 변수가 실행되는 SQL) 변수 값이 ?로 처리되서 들어오는 문제점은 있다.

db2pd를 통해서 SQL을 확인한다면

> db2pd –d sample –dynamic

Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:06:46 -- Date 03/12/2012 13:17:07

Dynamic Cache:
Current Memory Used           912084
Total Heap Size               1271398
Cache Overflow Flag           0
Number of References          184
Number of Statement Inserts   26
Number of Statement Deletes   8
Number of Variation Inserts   18
Number of Statements          18

Dynamic SQL Statements:
Address            AnchID StmtUID    NumEnv     NumVar     NumRef     NumExe     Text
0x00002B8015F1FD60 111    1          1          1          1          1          SELECT POLICY FROM
    SYSTOOLS.POLICY WHERE MED='DB2TableMaintenanceMED' AND DECISION='TableRunstatsDecision' AND NAME='TableRunstatsPolicy'
0x00002B8015E4F440 164    1          1          1          3          3          SELECT COLNAME, TYPENAME FROM
     SYSCAT.COLUMNS WHERE TABNAME='POLICY' AND TABSCHEMA='SYSTOOLS'
0x00002B8015F2CB80 167    1          1          1          1          1          SELECT IBM.TID, IBM.FID FROM
    SYSIBM.SYSTABLES AS IBM, SYSTOOLS.HMON_ATM_INFO AS ATM WHERE ATM.STATS_FLAG <> 'Y' AND IBM.TYPE IN ( 'S', 'T' ) AND
    ATM.CREATE_TIME = IBM.CTIME AND ATM.SCHEMA = IBM.CREATOR AND ATM.NAME = IBM.NAME ORDER BY IBM.FID, IBM.TID WITH UR
0x00002B8015E423C0 185    1          1          1          1          1          select * from emp
0x00002B8015F279A0 332    1          1          1          1          1          UPDATE SYSTOOLS.HMON_ATM_INFO
    AS ATM SET STATS_FLAG = 'N', REORG_FLAG = 'N' WHERE (ATM.SCHEMA, ATM.NAME) IN   (SELECT IBM.CREATOR,           IBM.NAME
    FROM SYSIBM.SYSTABLES AS IBM    WHERE IBM.TYPE = 'N' )
0x00002B8015EF7400 340    1          1          1          2          2          SELECT TABNAME FROM
    SYSCAT.TABLES  WHERE TABNAME='HMON_ATM_INFO' AND  TABSCHEMA='SYSTOOLS'
0x00002B8015E8CFE0 450    1          1          1          3          3          SELECT TRIGNAME FROM
    SYSCAT.TRIGGERS WHERE TABNAME='POLICY' AND TABSCHEMA='SYSTOOLS'
0x00002B8015F01E80 462    1          1          1          156        156        SELECT CREATE_TIME FROM
    SYSTOOLS.HMON_ATM_INFO WHERE SCHEMA = ? AND NAME = ? FOR UPDATE
0x00002B8015E469A0 513    1          1          1          1          1          select * from staff
0x00002B8015E4B640 580    1          1          1          3          3          CALL
    SYSPROC.SYSINSTALLOBJECTS('POLICY','V','','')
0x00002B8015F1B4E0 639    1          1          1          1          1          DELETE FROM
    SYSTOOLS.HMON_ATM_INFO AS ATM WHERE NOT EXISTS ( SELECT * FROM SYSIBM.SYSTABLES AS IBM WHERE ATM.NAME = IBM.NAME AND
    ATM.SCHEMA = IBM.CREATOR AND ATM.CREATE_TIME = IBM.CTIME ) WITH UR
0x00002B8015F23D80 734    1          1          1          1          1          UPDATE SYSTOOLS.HMON_ATM_INFO
    AS ATM SET ATM.STATS_FLAG = 'Y'
0x00002B8015E91DC0 735    1          1          1          2          2          CALL SYSINSTALLOBJECTS(
    'DB2AC', 'V', NULL, NULL )
0x00002B8015F016E0 766    1          1          1          1          1          SELECT CREATOR, NAME, CTIME
    FROM SYSIBM.SYSTABLES WHERE TYPE='T' OR TYPE='S' OR TYPE='N' WITH UR
0x00002B8015E91160 809    1          1          1          3          3          SET CURRENT LOCK TIMEOUT 5
0x00002B8015EFF380 876    1          1          1          1          1          UPDATE SYSTOOLS.HMON_ATM_INFO
    SET STATS_LOCK = 'N', REORG_LOCK = 'N'
0x00002B8015EFB680 886    1          1          1          2          2          SELECT TABNAME FROM
    SYSCAT.TABLES  WHERE TABNAME='HMON_COLLECTION' AND  TABSCHEMA='SYSTOOLS'
0x00002B8015F2EF00 1014   1          1          1          1          1          SELECT POLICY FROM
    SYSTOOLS.POLICY WHERE MED='DB2CommonMED' AND DECISION='NOP' AND NAME='CommonPolicy'

Dynamic SQL Environments:
Address            AnchID StmtUID    EnvID      Iso QOpt Blk
0x00002B8015F1FF20 111    1          1          CS  5    B
0x00002B8015E4F5C0 164    1          1          CS  5    B
0x00002B8015F2CDA0 167    1          1          CS  5    B
0x00002B8015E42500 185    1          1          CS  5    B
0x00002B8015F27BA0 332    1          1          CS  5    B
0x00002B8015EF7580 340    1          1          CS  5    B
0x00002B8015E8D160 450    1          1          CS  5    B
0x00002B8015F174A0 462    1          1          CS  5    B
0x00002B8015E46AE0 513    1          1          CS  5    B
0x00002B8015E4B7A0 580    1          1          CS  5    B
0x00002B8015F1B6C0 639    1          1          CS  5    B
0x00002B8015F23EE0 734    1          1          CS  5    B
0x00002B8015EF3CA0 735    1          1          CS  5    B
0x00002B8015F01860 766    1          1          CS  5    B
0x00002B8015E912A0 809    1          1          CS  5    B
0x00002B8015EFF4E0 876    1          1          CS  5    B
0x00002B8015EFB800 886    1          1          CS  5    B
0x00002B8015F2F080 1014   1          1          CS  5    B

Dynamic SQL Variations:
Address            AnchID StmtUID    EnvID      VarID      NumRef     Typ Lockname                   Val Insert Time                Sect Size

DAS 에 대한 블로그 글에 Windows 작업 스케줄러에서 DB2 자동화 방법에 대한 문의가 있었다. 최근 Windows 용 DB2를 설치했던 고객사에서도 관련하여 문의가 왔었다. 인터넷에서 관련 자료 검색이 잘 안되었나 하는 생각이 들기도 하여 블로그에 남겨 본다.

Windows XP에서 작업 스케줄러를 설정하는 것과 Windows 7 혹은 Vista 혹은 2008 Server에서 설정하는 방법이 약간 다르다.

DB2 관련 작업을 자동화하기 위해 개인적으로 2개의 파일을 만들어서 설정을 했다.

### XP  및 Windows 2003 서버의 경우 ####

1. cmd 파일 (파일명 예:  backup.cmd)

db2cmd /w /c /i db2 -tf backup.sql >> bak.txt

작업 스케줄러에서  backup.cmd 파일을 등록해서 실행시키면 된다.

실행여부는 bak.txt 로그 파일을 통해 수행 여부를 확인하면 된다.

 

2. db2 파일 (파일명 예: backup.db2)

    파일 확장자는 임의적이다.

!db2 "connect to sample" ;

!db2 "quiesce db immediate force connections" ;

!db2 "terminate" ;

!db2 "backup db sample to 'C:\'" ;

!db2 "connect to sample" ;

!db2 "unquiesce db" ;

!db2 "terminate" ;

위 명령어는 Offline Backup을 받기 위해서 DB에 접속된 연결을 강제 제거하여 DB를 비활성화(offline)시킨 후 백업을 수행하도록 쓰여졌다. (! 는 OS 레벨에서 수행되도록 하기 위한 DB2 환경에서의 escape 문자이다)

 

### Vista 이상  및 Windows 2008 서버의 경우 ####

작업 스케줄러 등록 방식이 바뀌었다.

시작 > 관리도구 > 작업 스케줄러 > 작업만들기 (창의 오른쪽 패널의 “작업”에 “작업 만들기” 선택)

* 동작 Tab  > 새로만들기

   - 프로그램/스크립트

“C:\Program Files (x86)\IBM\SQLLIB\BIN\db2cmd.exe

   - 인수 추가

db2 –tvf listdb1.db2

listdb1.db2 Script
!db2 "list db directory" >> db2list1.txt ;

   - 시작 위치   : listdb1.db2 위치

+ Recent posts