Post List

2015년 1월 8일 목요일

대용량 데이터베이스 솔루션 1권 #17 Array Processing

Array Proccessing (다중처리)

- 한번 DBMS CALL에 여러건의 DATA를 처리하는 방법
- DBMS CALL은 시스템 Overhead의 주범
- Array Processing은 시스템 Overhead를 감소
- ARRAY SIZE는 사용자가 지정 가능하나 지나치면 Overhead 발생
  (적절한 크기에서 10배 더 크게 잡아도 성능은 2배도 차이가 안남)
- 작성방법이 3GL과 차이가 많이 나므로 개발자들의 인식전환이 필요함



- 한번 FETCH 시 여러건을 동시에 ACCESS
- ACCESS와 동시에 DATA의 가공처리
- ACCESS 되면서 INSERT, UPDATE 구별
- 여러 건을 동시에 INSERT 혹은 UPDATE

Array Processing 예제

1. 상황

은행에서 일일 50만건의 손님의 업무를 봐준다.
신규 고객의 경우는 계좌정보에 INSERT를 해야하며, 기존 고객의 경우는 UPDATE를 해줘야 한다.

A, B Table의 벤다이어그램으로 생각을 해보면 겹쳐지는 부분은 UPDATE 영역이 될 것이며, A에만 속한 부분은 INSERT 영역이 될것이다.
UPDATE 시 보통 Unique Index를 활용하여 ROW를 ACCESS 하는데, 이건 정말 느린 방법이다. 대부분의 작업은 그전에 미리 SELECT 를 하므로 B  TABLE의 ROWID를 알아 낼수가 있다. ROWID로 접근을 하는것이 가장 빠른 방법이겠다. 한걸음 더 나아가 생각을 해본다면 ROWID 순으로 SORTING을 한다면 연속된 BLOCK에 UPDATE를 자주하게 됨으로 더 빠른 실행이 가능 할것이다.

2. Non Array Processing 예제
EXEC SQL_BEGIN DECLARE section;
  VARCHAR v_jongcode[8];
  INT     v_cprice;
  VARCHAR v_dsum_rowid[20];
EXEC SQL_END DECLARE SECTION;

EXEC SQL DECLARE c1 CURSOR FOR
  SELECT a.jongcode, a.cprice, b.rowid
    FROM dprice_c a, dsum_s b
   WHERE a.jongcode like '45%'
     AND a.jongcode = b.jongcode(+)
     AND a.cdate    = b.cdate(+);
EXEC SQL OPEN c1;
for(;;)
{
  EXEC SQL FETCH c1 INTO :v_jongcode, :v_cprice, :v_dsum_rowid;
  if (sqlca.sqlcode == 1403) break;
    EXEC SQL UPDATE DSUM_S SET csum = csum + :v_cprice
              WHERE ROWID = :v_dsum_rowid;
    if (sqlca.sqlcode == 1403)
    EXEC SQL INSERT INTO DSUM_S values (:v_jongcode, '19940829', :v_cprice);
  EXEC SQL COMMIT WORK;
}
EXEC SQL CLOSE c1;
EXEC SQL COMMIT WORK RELEASE;
위 예제는 한건 한건 따로 UPDATE 와 INSERT를 해주는 PL/SQL이다.
위와 같은 실행은 엄청난 DBMS CALL Overhead를 유발하여 실행시간이 엄청 느리다.
그럼 Array Processing 으로 할려면 어떻게 해야 할까 ?

3. Array Processing 예제
#define ARRAY_SIZE 100

EXEC SQL BEGIN DECLARE SECTION;
  INT     v_num[ARRAY_SIZE];
  VARCHAR v_fld1[ARRAY_SIZE][10];
  VARCHAR v_rowid[ARRAY_SIZE][20];
  SHORT   i_rowid[ARRAY_SIZE];
  INT     loop;
EXEC SQL END DECLARE SECTION;

...

EXEC SQL DECLARE C1 CURSOR FOR
  SELECT a.num1, a.fld1, b.rowid
    FROM TABLE1 a, TABLE2 b
   WHERE a.fld1 = b.fld2(+)
   ORDER BY B.ROWID;

EXEC SQL OPEN c1;

indt = 1;
whild (indt)
{
  EXEC SQL FETCH c1 INTO :v_num1, :v_fld1, :v_rowid, i_rowid;
  if (sqlca.sqlcode = 1403) indt = 0;
  loop = sqlca.sqlerrd[2] - num_ret;
  num_ret = sqlca.sqlerrd[2]; -- Reset the number

  -- row id is null at the first row fetched
  if (i_rowid[0] == -1) {
    process_up_ins(0); -- INSERT ONLY
  } else {
    -- rowid is null at the last row fetched
    if (i_rowid[loop - 1] == -1) {
      process_up_ins(1); -- UPDATE AND INSERT BOTH
    } else {
      process_up_ins(2); -- UPDATE ONLY
    }
  }
  EXEC SQL COMMIT WORK;
}
EXEC SQL CLOSE c1;
EXEC SQL COMMIT WORK RELEASE;

rrocess_up_ins(proc_flag)
int proc_flag;
{
  if (proc_flag == 2) {
    EXEC SQL FOR :loop UPDATE TABLE2 SET num2 = num2 + :v_num1
                        WHERE ROWID = :v_rowid;
  } else if (proc_flag == 1 {
    EXEC SQL FOR :loop INSERT INTO TABLE2
                       SELECT :v_fld1, :v_num1 FROM dual
                        WHERE :v_rowid IS NULL;
    EXEC SQL FOR :loop UPDATE TABLE2 SET num2 = num2 + :v_num1
                        WHERE :v_rowid IS NOT NULL
                          AND ROWID = :v_rowid;
  } else {
    EXEC SQL FOR :loop INSERT INTO TABLE2
                       VALUES (:v_fld1, :v_num1);
  }
}

ARRAY SIZE가 100개인 경우
IF      100개의 ARRAY 중 첫번쨰가 NULL이면 100개 모두 INSERT
ELSE 마지막이 NULL 이면 UPDATE, INSERT 같이 해야 하는 경우,
ELSE 100개 모두 UPDATE 로 실행한다.

중간에 UPDATE, INSERT 같이해야 하는 경우에는
ROWID가 NULL이면 INSERT, 아니면 UPDATE를 실행하면 된다.

작가
이화식
출판
엔코아컨설팅
발매
1996.03.01
평점
블로거의 오늘의 책에 참여한 포스트 입니다