고객사에 지원을 하다 보면, 현재의 Database를 다른 곳으로 옮기거나 특정 tablespace의 위치를 바꿔야 하는 경우가 발생한다.

이유는 디스크 공간이 부족해서 여유있는 곳으로 옮겨야 하는 상황이 되었거나, 외장 스토리지의 IO 성능 개선 작업을 하면서 Container로 사용 중인 Raw device의 경로가 변경되거나, 사용자 실수로 데이터가 삭제되서 데이터 복구를 하고자 하는 경우 등이라 할 수 있겠다.

DB 백업 이미지로 위와 같은 작업을 하기에는 시간이 걸린다. 최종 백업을 받고 새로운 곳으로 restore를 해야 되므로 어느 정도 소요시간이 발생한다.

이런 경우 db2relocatedb 유틸리티를 사용하면 백업/복구 방법보다는 작업 시간을 단축할 수 있다.

작업은 db2 중지 후 진행한다.

db2relocatedb 수행을 위한 설정 파일

DB_NAME=데이터베이스명
DB_PATH=데이터베이스 홈 경로
INSTANCE=인스턴스명
NODENUM=0
LOG_DIR=oldDirPath,newDirPath
STORAGE_PATH=스토리지 경로
CONT_PATH=콘테이너경로

<시나리오>

데이터베이스 명 : sample –> test

데이터베이스 위치 : /instance/inst10 –> /database

트랜잭션 로그 경로 : /instance/inst10/inst10/NODE0000/SQL00001/LOGSTREAM0000/

                             -> /database/test/active/

스토리지 경로 : /instance/inst10 –> /database/test/data

 

설정 파일 (db2relo.cfg )

DB_NAME=sample,test
DB_PATH=/instance/inst10,/database
INSTANCE=inst10
NODENUM=0
LOG_DIR=/instance/inst10/inst10/NODE0000/SQL00001/LOGSTREAM0000/,

              /database/test/active/
STORAGE_PATH=/instance/inst10,/database/test/data

 

위치 변경되는 파일들을 옮긴다.

$> cp -R /instance/inst10/ins* /database
$> cp -R ./LOGSTREAM00*/ /database/test/active/NODE0000/LOGSTREAM0000/
$> cp -R NODE00* /database/test/data/inst10
$>mv /database/test/data/inst10/NODE0000/SAMPLE/
         /database/test/data/inst10/NODE0000/TEST/

DB 위치 변경

$>  db2relocatedb -f db2relo.cfg

Files and control structures were changed successfully.
Database was catalogued successfully.
DBT1000I  The tool completed successfully.

변경 확인

# db2 list db directory

System Database Directory

Number of entries in the directory = 1

Database 1 entry:

Database alias                       = TEST
Database name                        = TEST
Local database directory             = /database
Database release level               = f.00
Comment                              =
Directory entry type                 = Indirect
Catalog database partition number    = 0
Alternate server hostname            =
Alternate server port number         =


# db2 get db cfg for test | grep -i "path to log files"
Changed path to log files                  (NEWLOGPATH) =
Path to log files                                       = /database/test/active/NODE0000/LOGSTREAM0000/

 

# db2 connect to test

   Database Connection Information

Database server        = DB2/AIX64 10.1.1
SQL authorization ID   = INST10
Local database alias   = TEST

 

# db2pd -db test -storage

Storage Group Paths:
Address            SGID  PathID    PathState    PathName
0x0A00020010BC0000 0     0         InUse        /database/test/data

 

# db2 "select count(*) from emp"

1
-----------
         42

  1 record(s) selected.

 

* DB 홈 경로 및 트랜잭션 로그 경로, 스토리지 경로의 경우 DB 생성 시 DB2가 자동으로 생성하는 경로명들이 있기 때문에 번거로울 수 있다. 경우에 따라서는 파일 옮기는 작업이 복잡하기 때문에 주의가 필요하고, 특정 콘테이너 단위로 변경하는 작업 정도로 사용하는 것이 안전하다 할 수 있겠다. (위 작업 수행 시, 원본을 copy 해서 db2relocatedb 작업 후 문제없는 경우 원본을 삭제해야 할 것이다.)

 

db2relocatedb 작업 중 일부가 번거로운 경우, 경로재지정 복구를 통하여 수행할 수 있다.

$> db2 backup db test to /work/backup compress
Backup successful. The timestamp for this backup image is : 20121109141428

 

경로 재지정 복구를 위한 복구 스트립트를 생성한다.

$> db2 restore db test from /work/backup taken at 20121109141428 redirect generate script db2redir.clp

 

복구 스트립트를 수정한다.

데이터베이스 명 : test –> sample

데이터베이스 위치 : /database –> /instance/inst10

트랜잭션 로그 경로 :  /database/test/active/ –> /instance/inst10/inst10/NODE0000/SQL00001/LOGSTREAM0000/

스토리지 경로 : /database/test/data –> /instance/inst10

RESTORE DATABASE TEST
FROM '/work/backup'
TAKEN AT 20121109141428
ON '/instance/inst10'
DBPATH ON '/instance/inst10'
INTO SAMPLE
NEWLOGPATH '/instance/inst10/inst10/NODE0000/SQL00001/LOGSTREAM0000/'
REDIRECT
WITHOUT ROLLING FORWARD
;
SET STOGROUP PATHS FOR IBMSTOGROUP
ON '/instance/inst10'
;

RESTORE DATABASE TEST CONTINUE;

restore 실행

$>  db2 -tvf db2redir.clp

RESTORE DATABASE TEST FROM '/work/backup' TAKEN AT 20121109141428 ON '/instance/inst10' DBPATH ON '/instance/inst10' INTO SAMPLE NEWLOGPATH '/instance/inst10/inst10/NODE0000/SQL00001/LOGSTREAM0000/' REDIRECT WITHOUT ROLLING FORWARD
SQL1277W  A redirected restore operation is being performed. During a table
space restore, only table spaces being restored can have their paths
reconfigured. During a database restore, storage group storage paths and DMS
table space containers can be reconfigured.
DB20000I  The RESTORE DATABASE command completed successfully.

SET STOGROUP PATHS FOR IBMSTOGROUP ON '/instance/inst10'
DB20000I  The SET STOGROUP PATHS command completed successfully.


RESTORE DATABASE TEST CONTINUE

DB20000I  The RESTORE DATABASE command completed successfully.

 

확인

$> db2 list db directory

Database 2 entry:

Database alias                       = SAMPLE
Database name                        = SAMPLE
Local database directory             = /instance/inst10
Database release level               = f.00
Comment                              =
Directory entry type                 = Indirect
Catalog database partition number    = 0
Alternate server hostname            =
Alternate server port number         =

$> db2 connect to sample

$> db2pd -db sample –storage

Storage Group Paths:
Address            SGID  PathID    PathState    PathName
0x0A00020010BC0000 0     0         InUse        /instance/inst10

에러라고 봐야할 지, 경고 성 메시지로 메시지를 봐야할 지 개인적으로도 아리송하지만, pureScale 환경에서 진단로그(db2diag.log)에 다음과 같음 메시지를 볼 수 있다.

2012-10-08-15.18.39.607132+540 I35440458A354 LEVEL: Error

PID : 13107340 TID : 1 PROC : db2havend

INSTANCE: db2inst1 NODE : 000

HOSTNAME: dbcni2

EDUID : 1

FUNCTION: DB2 UDB, high avail services, GPFSCluster::refreshVersionInfo, probe:5127

DATA #1 : String, 37 bytes

detected DB2_CFS_GPFS_NO_REFRESH_DATA

pureScale에 구성된 member와 CF의 상태 확인을 위해 보통 db2instance –list 명령어를 사용한다. 그런데 위 명령어가 결과를 반환하는데 약간의 시간이 걸린다. 약 3초 정도…

그래서 실행 시간을 빠르게 하기 위해 DB2_CFS_GPFS_NO_REFRESH_DATA=true 설정을 .profile에 할 수 있다.

위 변수에 대한 정보는 찾아 볼 수 없다. 다만, db2instance –list 를 수행하는 시점에 최신의 데이터를 수집해서 가져 오느냐? 아니면 이미 수집되어 있는(아마도 DB2 내부적으로 주기적으로 수집을 해 놓는 간격이 있어서) 데이터를 가져 오느냐? 의 차이로 짐작하고 있다.

개인적으로 체감하기에 신속한? 결과를 가져오는 것 같지는 않지만, 이 변수를 설정하고 나면 진단로그에 위와 같은 메시지가 기록이 된다. (V10.1 FixPack 0 기준)

여러 가지 테스트를 해 본 바, DB2_CFS_GPFS_NO_REFRESH_DATA 가 적용되는 시점은 db2start 시점은 아니고 db2instance –list 적용 시점에 반영이 된다.

테스트한 환경에서는 . .profile 등을 수행하여 환경 변수를 적용했을 때는 적용되지 못했고, 새롭게 인스턴스 계정으로 switch 한 경우 적용되었다.

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 (결과 집합을 한번 더 감싸서 짤라내는)로 만들어 처리할 수 밖에는 없다. (페이징처리를 하는 기준 컬럼이 인덱스를 활용할 수 있다면 다른 방법으로 처리할 수도 있겠지만..)

고객사 지원 시, DB2 계정 정책은 DB 레벨에서 설정한다.

설치 작업만 하는 경우는 대부분 고객사에서 인스턴스 계정으로 업무용으로도 사용하는 것 같고, 프로젝트 업무를 지원하게 되면 테이블 공간까지 정의를 하면서 계정 정책을 수립하게 된다.

보통은 DBADM 권한을 부여해서 사용하기에 크게 문제가 되지 않았는데, DBADM 권한을 주지 않고 더 약한 권한을 주는 경우 다음과 같은 문제를 겪을 수 있다.

필자의 경우는 V10.1 에서 겪은 문제이나, 그 이하 버전에서도 접할 가능성이 있어 보인다.

계정 생성

db2 “grant CONNECT,DATAACCESS on database to USER db2user

테이블 조회

db2 connect to sample user db2user using db2user
db2 "select count(1) from 스키마명.employee"
SQL5193N  The current session user does not have usage privilege on any enabled workloads.  SQLSTATE=42524

(참고) 본 테스트는 약간 시나리오가 인위적으로 구성되어서 테스트해본다면 재현되지 않을 수 있다.

위 문제로 해당 테이블에 테이블 레벨 권한을 부여(INDEX,INSERT,SELECT,UPDATE,DELETE) 했으나 동일한 메시지가 반환되었다.

문제는 DB2가 기본적으로 제공하는 Workload 때문이였다.

아래처럼 2개의 workload가 DB가 생성되는 시점에 생성된다. Admin용 워크로드와, User용 워크로드…

db2 "select substr(workloadname,1,30) wlname from syscat.workloads"

WLNAME
------------------------------
SYSDEFAULTUSERWORKLOAD
SYSDEFAULTADMWORKLOAD

 

db2 "select substr(a.workloadname,1,30) wlname,substr(b.grantor,1,20) grantor, substr(b.grantee,1,20) grantee, b.granteetype,usageauth from syscat.workloads a, syscat.workloadauth b where a.workloadid=b.workloadid"

WLNAME                         GRANTOR              GRANTEE              GRANTEETYPE USAGEAUTH
------------------------------ -------------------- -------------------- ----------- ---------
SYSDEFAULTUSERWORKLOAD        SYSIBM               PUBLIC               G           Y

 

문제가 난 시점에는 위 script를 조회하지 않아 정확히 설정된 값이 어떻게 되어 있는지 모르지만, 예측하기로는 SysDefaultUserWorkload의  GRANTEE가 PUBLIC 으로 되어 있지 않았을 것 같다.

(V9.1 부터 제공되는 DB 생성시 restrictive 옵션과 V9.5에서 제공된 Default Workload 영향으로 위 값는 상황에 따라 다를 수 있을 것 같다. 위 문제가 발생한 DB는 restrictive 옵션을 주면서 DB를 생성하였다.)

 

V9.7 정보센터의 내용을 살펴보니 다음과 같이 정의되어 있다.

기본 사용자 워크로드 : DB 서버에 접속 시 모든 접속에 대해 적용되는 워크로드

기본 관리자 워크로드 : 관리작업을 수행할 때 허용하게 할지 말지를 결정하는 워크로드

 

그래서 위의 문제는 다음과 같이 권한 부여를 하여 정상 실행됨을 확인하였다.

db2 “grant usage on workload sysdefaultuserworkload to user db2user

 

db2 connect to sample user db2user using db2user

db2 "select count(1) from db2inst1.employee"

1
-----------
         42

  1 record(s) selected.

락 상태를 확인하거나 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/

“클러스터 관리자 자원 상태가 불일치하므로 db2start가 실패했습니다.”

상세 설명

클러스터 관리자 자원 모델과 db2nodes.cfg 파일이 일치하지 않으므로 db2start 명령으로 프로세스를 시작할 수 없습니다. 클러스터 관리자와 동기화되지 않은 db2nodes.cfg를 수정하면(고의적으로 또는 비고의적으로) 이러한 불일치가 발생합니다.

불일치가 해결될 때까지 DB2 pureCluster 인스턴스 또는 새 자원을 시작할 수 없습니다. 이미 시작된 DB2 자원은 이 오류에 영향을 받지 않습니다.

처리방법

db2nodes.cfg 파일을 이전 구성(클러스터 관리자와 동기화된 구성)으로 복원하십시오. 이전 구성으로 복원할 수 없으면 클러스터 자원 모델을 복구하십시오. 이를 수행하려면 전역 db2stop을 발행하여 인스턴스를 중지하고 -repair 옵션을 사용하여 db2cluster 도구를 실행하십시오.

DB2 V10.1 pureScale 설치 후 사용 중, Infiniband Adapter 기능코드(Feature Code)가 다르게 물려 있어서 Adapter를 교체하는 작업을 하였다.

이후 db2start 시 SQL1517N 메시지가 발생하면서 기동되지 못하였다. db2cluster 명령어를 통하여 repair 작업을 하면서 정상화할 수 있었다.

root 사용자로 db2 엔진이 설치된 경로로 이동하여 다음과 같이 작업을 수행하였다.

#> cd /db2/v10.1/bin
#> ./db2cluster -cfs -list –domain  (클러스터 도메인 명 확인)
#> ./db2cluster -cm -repair –domain 도메인명

 

db2cluster –repair 옵션 시, tsa 클러스터 구성(quorum, domain) 관련하여 초기화 후 재 설정을 하는 것 같다.

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

그러나 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.

+ Recent posts