개발자의 끄적끄적

[cubrid] PHP를 이용하여 CUBRID 사용할 때의 주의점 [펌] 본문

개발/sql

[cubrid] PHP를 이용하여 CUBRID 사용할 때의 주의점 [펌]

효벨 2020. 5. 11. 01:00
728x90
반응형

[cubrid] PHP를 이용하여 CUBRID 사용할 때의 주의점 [펌]

 

 

CUBRID DBMS 를 사용하여 PHP 응용을 개발할 때의 MySQL 과의 차이점 때문에 발생할 수 있는 문제에 대해 요약 정리하고자 한다.

 

1. Select 절이라도 꼭 트랜잭션 처리를 해주자 . ( Commit/Rollback 을 해주자 ).

 

  대부분의 MySQL 개발자의 경우 Select 절을 수행후 Transaction 처리를 않하는 것을 습관적으로 사용하고 있다.

  그러나 CUBRID는 이의 경우에라도 꼭 처리를 해주어야 한다. ( 이는 트랜잭션에 처리에 관한 정석을 따른다고 할까? )  

 

  CUBRID 도 특별히 문제가 발생하는 것은 아닌데, 문제는 어떤 테이블에 대해 전체 삭제 등을 처리 할 경우에 문제가 발생한다.

  이는 CUBRID의 PHP API 가 사용하는 CCI 라는 Interface 를 사용하게 되는데, 아직 CCI 는 autocommit 을 지원하지 않기 때문에 발생한다.

  JDBC 인터페이스는 autocommit 을 지원하기 때문에 autocommit 모드 하에서는 아래와 같은 문제가 발생하지 않는다.

 

  최근에 맡고 있는 어떤 서비스에서 Query 들이 테이블 Lock 을 기다리면서 서비스가 먹통이 되는 현상이 발견되었다.

  이것의 문제는  Insert/Update/Delete 등의 경우에는 트랜잭션 처리를 해주어서 별 문제가 되지 않는데, 새벽시간을 통해 매일 그 날의 통계정보를 갱

  신하는 테이블에 대한 작업 때문에 발생하였다. 작업은 통계 테이블을 모두 삭제하고, 갱신된 통계정보를 입력하는 질의였다.  아래의 문제로 인해 서비

  스  장애가 발생하였으나 아래의 내용을 잘 보면 피해 갈수 있는 방법이 있다.

 

  간단히 재현하면 아래와 같다.

 

  트랜잭션 T1, T2, T3 가 있다고 가정한다. 재현테이블은 foo 라고 하자.

  T1 : select ... from foo , 또는 foo 를 포함하는 조인 질의 수행,

  T2 : delete from foo 실행 , 조건없는 delete 테이블 작업.

  T3 : select ... from foo 를 수행.                  

 

T1 : autocommit 을 끈 상태에서 select 질의를 수행한다. 

  

 csql> ;au off
AUTOCOMMIT IS OFF
csql> select * from foo
csql> ;x

=== <Result of SELECT Command in Line 2> ===

            a  b
===================================
            1  'aaa       '
            2  'bbb       '
            3  'ccc       '
            4  'ddd       '
            5  'eee       '
            6  'fff       '
            7  'ggg       '
            8  'hhh       '
            9  'iii       '
           10  'jjj       '


10 rows selected.

1 command(s) successfully processed.
csql>

  

이 경우의 lockdb 를 살펴보면.  foo 테이블에 대해 IS_LOCK 이 잡힌다.  질의 가 수행되고 commit/Rollback 이 수행되면 IS_LOCK 은 놓게된다.

 OID =  0|   551|   2
Object type: Class = foo.
Total mode of holders =   IS_LOCK, Total mode of waiters = NULL_                                  LOCK.
Num holders=  1, Num blocked-holders=  0, Num waiters=  0
LOCK HOLDERS:
    Tran_index =   1, Granted_mode =  IS_LOCK, Count =   3, Nsub                                  granules =  0

 

테이블에 IS_LOCK 이 잡힌 상태에서 해당 테이블을 모두 삭제해본다.

 

이경우가 T2이다.

 

T1이 끝나기 전에 T2 가 수행되면 T2 는 테이블 전체 삭제이므로 IX_LOCK 단계에서 X_LOCK 을 잡기 위해 T1이 IS_LOCK 을 놓기 위해 기다린다.

 

이 때의 LockDB 정보는 아래와 같다. T2 는 Lock Timeout 이 300 으로 설정되어 300초 동안 기다리다가 aboart 된다.

 

 OID =  0|   551|   2
Object type: Class = foo.
Total mode of holders =    X_LOCK, Total mode of waiters = NULL_LOCK.
Num holders=  1, Num blocked-holders=  1, Num waiters=  0
LOCK HOLDERS:
    Tran_index =   1, Granted_mode =  IS_LOCK, Count =   2, Nsubgranules =  0
BLOCKED LOCK HOLDERS:
    Tran_index =   2, Granted_mode =  IX_LOCK, Count =   2, Nsubgranules =  0
                      Blocked_mode =   X_LOCK
                      Start_waiting_at = Tue Dec  1 16:54:04 2009
                      Wait_for_nsecs = 300000

 

T1 이 IS_LOCK 을 놓기 전에는 T2 가 실행이 되질 않는다. 

여기에 해당 테이블에 Select 질의를 수행하는 T3 가 실행된다.

 

이 경우에 T3 는 또한 T2 가 끝나기를 기다린다.

 OID =  0|   551|   2
Object type: Class = foo.
Total mode of holders =    X_LOCK, Total mode of waiters =   IS_LOCK.
Num holders=  1, Num blocked-holders=  1, Num waiters=  1
LOCK HOLDERS:
    Tran_index =   1, Granted_mode =  IS_LOCK, Count =   2, Nsubgranules =  0
BLOCKED LOCK HOLDERS:
    Tran_index =   2, Granted_mode =  IX_LOCK, Count =   2, Nsubgranules =  0
                      Blocked_mode =   X_LOCK
                      Start_waiting_at = Tue Dec  1 16:54:04 2009
                      Wait_for_nsecs = 300000
LOCK WAITERS:
    Tran_index =   3, Blocked_mode =  IS_LOCK
                      Start_waiting_at = Tue Dec  1 16:57:50 2009
                      Wait_for_nsecs = 300000

 

이 상황이 되면 T2 가 끝나기 전에 T3, 그 뒤에 오는 Select 질의 들이 모두 T2 가 끝나기를 기다리게 되는데,  이럴 경우에 T2 가 끝나기전에는 마냥 기다리게 된다. LockTimeout 이 300 초가 흐른 뒤에 보면 아래와 같은 오류가 찍히고, T3 는 수행이 된다.  이럴 경우에는 적어도 300초가 흐르면 수행은 되지만 만약 LockTimeout 이 -1 이면 DB는 먹통이 되는 것 처럼 보이게 된다.

 

 csql> delete from foo
csql> ;x

In the command from line 1,

ERROR: Your transaction (index 2, dba@cdbs014.cub|23041) timed out waiting on    X_LOCK lock on class foo. You are waiting for user(s) dba@cdbs014.cub|22983, dba@cdbs014.cub|23069 to finish.

 

이를 해결할 수 있는 방법은 1차적으로는 모든 select 질의에 대해서도 Commit/Rollback 을 명시해 주는 것이다. 현재 CUBRID의 PHP API 의 autocommit 이 지원될 때 까지는 , 2차 방법으로는 T2 에 대해 테이블 Lock 을 잡지 않도록 하는 것이다. 이럴 경우에는 T3 , T4 계속 들어오는 트랜잭션이 잘못된 것을 볼 수 있을 수도 있는데, 서비스가 멈추는 현상은 발생하지 않을 것이다.

Index scan 을 하면서 삭제를 한다면 테이블락을 잡지 않고 인스턴스( 튜플 ) 락을 잡기 때문에 뒤에오는 Select 질의에는 영향이 없다.

 

따라서 MySQL 로 PHP를 개발하던 개발자들이 CUBRID 를 사용함에 있어서 유의 할 점 하나는 Select 절이어도 꼭 Commit/Rollback 을 처리해 줘야 한다.

 

위와 같은 현상이 발생하지 않도록 어떤 DBMS 를 사용할 때에도 꼭 트랜잭션 처리는 중요하다.

 

※ Lock 관련 메뉴얼 ( http://www.cubrid.com/online_manual/cubrid_820/syntax/syntax_tran_lock_intro.htm ) 를 참조 바랍니다.

 

2. select 질의 수행해주고 request handle 을 꼭 닫자.

 

select 질의를 수행하면 CUBRID 의 client 를 처리하는 cub_cas 라는 프로세스가 요청한 질의에 대한 Parsing 을 수행하여 Prepare statement 및 결과 ResultSet 등을 메모리에 담고 있다.

 

select 질의를 cubrid_execute 를 실행한 후 해당 request handle 을 close 를 해주지 않으면 cub_cas 의 메모리가 급격히 증가하게 되고, 해당 connection 이 여러개의 cub_cas 에 연결되어있을 경우 서버의 리소스를 점유하여 성능 저하 현상이 발생한다.

 

따라서 select 질의 수행후 해당 request handle 에 대해 fetch 가 끝나면 아래 예제와 같이 꼭 close 를 해주어야 한다. 

 

 $con = cubrid_connect ("dbsvr.cubrid.com", 12345, "demodb");
if ($con) {
   echo "connected successfully";
   $req = cubrid_execute ( $con, "select * from members",
                           CUBRID_INCLUDE_OID | CUBRID_ASYNC);
   if ($req) {
      while ( list ($id, $name) = cubrid_fetch ($req) ){
         echo $id;
         echo $name;
      }
      cubrid_close_request($req);
   }
   cubrid_disconnect($con);
}

 

 

출처 : https://m.blog.naver.com/PostView.nhn?blogId=windyhan&logNo=110075051208

반응형
Comments