배치 어플리케이션 개발시 고려사항
배치 어플리케이션 개발시 고려사항
배치 아키텍처가 준비되었다면 실제 배치 어플리케이션을 설계/개발해야 한다. 이를 위해서 고려해야될 사항을 살펴본다.
데이터 설계
배치에서는 대용량의 데이터를 다루기 때문에 이부분에 대한 고민이 많이 필요하다.
Resource유형
File
기본적으로 배치는 데이터를 순차적으로 처리하기 때문에 File처리가 DB처리보다 속도에 장점이 있다. 또한 다른 시스템으로 처리결과를 전달하기 위해서는 일반적으로 파일전송을 많이 사용한다. File을 생성하거나 참조할때에는 Charset에 주의해야 한다. 또한 File Input/Output 레이아웃 정의시 한글이 포함된 컬럼의 경우 byte length가 달라지는 것을 감안해야 하며 숫자컬럼을 표현할 때에는 부호,소수점에 따라서 교정이 필요할 수 있다.
- Fixed Length : 레코드의 각 컬럼이 고정길이로 구성되어 있다. length로 각 컬럼을 구분한다. (ex, SAM파일)
- Variable Length : 레코드의 각 컬럼이 가변길이로 구성되어 있다. Column Separator로 각 컬럼을 구분한다. (ex, CSV파일)
DB
DB의 경우 Connection을 얻고 PreparedStatement를 생성하여 쿼리를 날리는 작업이 필요하기 때문에 상대적으로 느릴 수 있으나 배치작업을 통해서 생성된 데이터가 다른 곳에서 많이 참조가 될 경우에는 데이터 Access에 이점이 있다. 또한 File 처리에 비해서 개발방법이 쉽고, 변경이 발생해도 빠르게 반영할 수 있다.
CHAR vs VARCHAR
과거 COBOL을 사용했던 시스템에서는 가변형이라는 데이터 타입이 존재하지 않았기 때문에 CHAR타입이 아직도 많이 남아있다. 고정길이 데이터에는 CHAR타입을 사용하는 것이 원칙이지만 데이터 타입은 언제 어떻게 변경될지 모르기 때문에 차세대 이후부터는 CHAR타입보다는 VARCHAR타입을 사용하는 것을 권장한다. 예로 CHAR형으로 데이터가 선언되어 있을 경우 남는 공간에 공백문자가 들어가게 되는데 이는 저장공간에 낭비가 발생할 뿐만 아니라 값비교를 위해서 trim등의 스트링 연산이 추가되어야 한다. 대용량 데이터를 처리해야하는 경우 이에 대한 비용도 무시할 수 없다. 운영시 용이성과 개발시 용이성을 볼 때 VARCHAR로 통일하는 것을 추천한다.
INDEX 재설계
INDEX는 데이터 Access를 빠르게 할 수 있으나, 데이터 추가/삭제/수정이 빈번하게 발생할 경우 속도저하를 발생시킨다. 온라인에서 사용하는 테이블과 최대한 겹치지 않도록 배치 테이블을 설계하는 것이 바람직하며 부득이하게 같은 테이블을 사용할 경우 적절하게 INDEX를 제어하도록 한다. (테이블당 3~4개 권고) 또한 SELECT를 사용할때 테이블 대부분의 자료를 읽어야 하는 경우에는 Full Scan하는 것이 더 빠를 수 있다. 그리고 배치작업에서 대용량 데이터를 적재할 경우에는 DROP INDEX를 하고 데이터 적재를 마친 뒤 INDEX재생성을 하는 것이 일반적으로 속도에 이점이 있다.
파티션 테이블 활용
데이터 저장공간에 파티션 개념을 도입하여, 테이블의 특정컬럼을 기준으로 하여 분리된 파티션에 데이터를 저장할 수 있도록 하는것이다. 데이터 로드, INDEX생성, 백업 및 복구등을 파티션 레벨에서 수행할 수 있으며 수행시간을 단축시킬 수 있다.특히 주기단위로 대용량 데이터를 관리하는데 매우 유용하며 쿼리를 파티션 테이블에서 수행할 경우 분할된 파티션들 중 일부에만 접근하기 때문에 성능이 향상된다. 병렬 프로그래밍과 같이 사용할 경우 효과를 극대화할 수 있다.
비지니스 로직 구성
비지니스 로직구성이 배치 어플리케이션의 핵심이다. 전체적으로 업무 프로세스를 재설계하는지가 관건이다.
업무 프로세스 재설계
기존 업무의 분석과 재설계를 통해 비지니스 로직자체를 단순화 한다. 유사한 업무를 통합하고, 불필요한 반복작업을 제거해야 한다. DBMS의 신규기능(ex, sum, over)을 활용할 수 있으며, 어플리케이션을 효율적으로 작성해야 한다. 반복문내의 불필요한 연산을 제거하고 I/O를 최소화해야 한다.
Application 효율화
loop 문내 불필요 로직 최소화
- Loop내에서 DB건당 Read는 최소화 하는 것이 좋다. JOIN쿼리를 활용하여 처음에 다수의 필요한 테이블 자료를 조회하면 DB Read횟수를 줄일 수 있다.
- 변경 전
while(DB.ReadA()){ 비지니스로직처리 DB.ReadB(); }
- DB.READ.A=> SELECT * FROM A
- DB.READ.B=> SELECT * FROM B WHERE NAME=?
- 변경 후
while(DB.ReadC()){ 비지니스로직처리 }
- DB.READ.C=> SELECT * FROM A,B WHERE A.NAME=B.NAME
- Loop내에서 DB건당 Write는 최소화 하는 것이 좋다. 로직처리 후 데이터를 모아서 처리하는 것을 권장한다. 또한 Java의 경우 JDBC addBatch기능을 활용하면 2~10배정도 속도향상이 있다.
- 변경 전
void a(){ 비지니스 로직 } void b(){ DBWrite();}
while(DBRead()){ a(); b(); }
- 변경 후
while(DBRead()){ a(); }
while(DBRead(a의결과){ b(); }
- 불필요한 객체생성이나 연산을 제거한다. 대표적으로 String연산을 loop내에서 반복할 경우 메모리 사용량 및 처리속도에 악영향을 줄 수 있다. MultiThread일 경우 StringBuffer를, SingleThread일 경우 StringBuilder를 사용하는 것이 일반적이다. 그 외에도 loop내에서 객체하나로 값만 바꿔서 사용할 수 있는 경우는 new를 loop문 밖으로 빼는 것이 필수이다.
단순 코드성 정보 Memory 활용
자주 참조되는 데이터(ex, 코드성 데이터, 영업일등)는 Memory이 Loading후 로직에서 활용하면 매번 DB Read를 하는 것보다 속도향상의 효과가 있다. 단, Loading되는 데이터의 사이즈를 감안해야 한다.
병렬처리를 감안한 파라미터 설계
- Multi Process
- Multi Thread