Microsoft 社는 2022년 6월 15일 Internet Explorer 11의 지원을 종료했습니다.

batch 프로그램 작성과 관련한 짧은 생각

제목

batch 프로그램 작성과 관련한 짧은 생각

수 많은 종류의 batch 프로그램이 있겠지만, 여기서 논하는 것은 데이타를 처리하는 batch 프로그램에 대한 것이다.

1. 어떤 기술(언어)로 batch 프로그램을 작성해야 하나?

여기서 기술하는 내용은 간단한 데이타 작업에는 적용되지 않는다. 그런 것들은 그 작업을 수행하는 프로그래머가 알아서 할 일이다.

1.1. 하면 안되는 것

1.1.1. 기술을 선택 할 때

익숙하지 않는 기술을 고려하고 있다면, 그것이 최선인지 숙고해야 한다.

실무에서 가장 많이 발생하는 사례는 Java 프로그래머 위주의 개발팀에서 프로시저를 사용하는 것이고, 프로그래머 출신이 아니거나 Java 프로그래머 출신이 아니거나 기타 Java 프로그래밍에 대한 이해가 부족한 관리자들이 이런 방향으로 나아가는 경향이 있다.

프로시저를 사용하면, 다소간의 성능 향상을 꾀할 수 있겠지만, 이건 프로시저가 최적화되었다는 것을 전제로 한 이야기이다.

프로시저에 익숙치 않은 대다수의 Java 프로그래머들은 로그를 남기는 것조차 힘들어 하고, 예외처리 뿐만 아니라 성능 측면에서도 부정적인 결과를 낳을 가능성이 높다.

데이타 자체에 익숙한 DBA 가 있다면, DBA 가 프로시저를 작성해도 되겠지만, 조직마다 DBA 의 역활이 다르다.

Java 프로그래머는 Web 프로그램을 개발한다고 하더라도 프로시저 따위에 익숙하지 않은 것이 일반적이고, 같은 프로시저라고 하더라도 데이타베이스에 따라 예외 처리 같은 것에서 중요한 차이가 난다.

예외 처리 같은 것만 차이가 나는 것은 아니다. Oracle 의 경우 프로시저에서 사용한 table 을 변경(alter) 한 경우, 프로시저를 다시 build 해야 하지만, PostgreSQL 은 그렇지 않다(런타임에서 에러가 발생할 것이고, 대부분 이게 더 괴롭다).

1.1.2. 구현 할 때

select 후에 loop를 돌리면서 insert 혹은 update 하는 일은 최소화해야 한다.

이런 방식은 프로시저, Array Processing 등 어떤 것을 갖다 붙여도 소용이 없다.

1.2. 해야 하는 일

데이타 처리는 insert-select 혹은 update-select 를 활용한다. index가 최적화되어 있다면 10배 이상의 성능향상이 가능한 경우도 있다.

개발팀이 Java 프로그래머 위주로 구성되어 있다면 Java로 작성한다.

개발팀내의 실무자들이 익숙한 기술을 사용하라는 취지이다.

2. batch 프로그램 작성할 때 참고사항

성능에 가장 결정적인 영향을 미치는 것은 commit 과 index 에 대한 것이다.

2.1. commit

auto commit 은 안된다.

commit 은 명시적으로 해야 하고, 예외가 발생하면 전체를 rollback 하는 것이 원칙이다.

이것이 성능에도 유리하다.

많은 변경사항을 한번에 commit 하려고 시도하면 에러가 발생하는 경우가 있는데, 그래도 auto commit 은 안되고, 중간중간에 명시적으로 commit 해야 한다.

2.2. Dynamic SQL(PreparedStatement)를 사용하라.

Java 프로그래머들은 PreparedStatement 를 관행처럼 사용한다.

프로시저는 CallableStatement를 사용하겠지만, PreparedStatement 와 유사한 방식으로 사용한다.

Statement는 성능에도 악영향도 있겠지만, 주석(--, /**/)이나 Single quotation marks 를 처리하는 등의 번거로운 일들도 기다리고 있다.

2.3. index 는 잠시 disable 시킨다.

insert 하는 테이블은 index를 disable 시켰다가, batch 가 끝난 후 다시 enable 해야 한다.

데이타베이스 제품 혹은 index 의 성격에 따라 unusable 시킨 다음 rebuild 하는 경우도 있고, 경우에 따라서는 drop 했다가 다시 생성하는 경우도 있다.

update 의 경우 where 절에서 사용되는 index 는 살려둬야 하고, select 구문의 where 절에 있는 index 도 마찬가지이다.

index는 데이타를 빠르게 찾기 위한 것이고, insert/update 할 때는 index 데이타를 생성하는 비용이 발생하므로, 복잡한 batch 프로그램은 disable 시켜야 할 index 를 선택하는 것이 중요한다.

다만, 실시간으로 실행되는 batch 에서는 index 를 disable 시키는 방법을 사용할 수는 없다.

프로그램적으로 DDL 구문을 실행하는 것은 원칙적으로 허용되지 않고, 그 index를 사용하는 다른 프로그램의 성능에도 영향을 미치게 될 것이다.

2.4. Array Processing(addBatch / executeBatch)

JDBC 에도 Array Processing(addBatch / executeBatch) 이 있다.

ProC 이야기는 할 필요는 없다. Java 보다 ProC 가 익숙하다면 그건 알아서 할 일이고, 성능 측면에서 매우 큰 차이는 없다.

Array Processing(addBatch / executeBatch) 을 사용하면, select 후에 loop를 돌리면서 insert 혹은 update 해야 하는 상황에서 약간의 성능 향상을 꾀할 수 있다.

소스코드의 변경도 많지 않다. 많은 변경사항을 한번에 반영하려고 시도하면 에러가 발생할 수 있다는 것만 주의하면 된다.

다음의 코드가 끝이다.

PreparedStatement pstmt = conn.prepareStatement(sql);
for(int i = 0; i < 100000; i++) {
pstmt.setInt(1, i);
// pstmt.executeUpdate();
pstmt.addBatch();
if(i % 10000 == 0) {
pstmt.executeBatch();
}
}
pstmt.executeBatch();
conn.commit();

pstmt.executeUpdate()pstmt.addBatch() 로 대체되었고, 매 1만건마다 그리고 마지막에 pstmt.executeBatch() 를 호출하면 끝이다.

2.5. 성능을 위한 마지막 수단

select 후에 loop를 돌면서 insert 하는 경우, insert 쪽에서 병목이 발생하면서 성능이 크게 떨어지는 경우에 한해, 검토할수 있는 마지막 방법이 있다.

그것은 프로그램적으로 PostgreSQL 의 pg_dump 결과와 같은 형식의 파일 생성한 다음, psql 유틸리티를 이용해서 데이타를 입력하는 것이다.

PostgreSQL 은 copy 명령어를 잘 활용할 필요가 있다.

Oracle 이라면 SQL* Loader 의 data file 을 만드는 프로그램을 작성한 다음, sqlldr 을 이용해서 그 결과 파일을 Oracle 에 올리는 방식이다.

1개월 걸리던 것을 3~4 시간으로 줄인 적도 있다.

이런 프로그램은 Escape character 를 적절히 처리해야 하고, 여러 번의 테스트를 감수해야 한다.

다만 이 방법은, 하면 안되는 것의 범주일 가능성도 있고, 프로그램 작성에 오랜 시간이 걸릴 수 있으므로, 마지막 수단으로만 사용해야 한다.

예를 들면 이기종 데이타베이스간의 데이타 이동이고, 실행시간이 너무 오래 걸려서 이에 대한 특별한 처리를 위한 복잡한 코드가 추가되는 경우를 의미한다.

3. 다른 종류의 batch 프로그램들도 Java 로 작성하는 것을 권장하는 이유

Linux 경험이 풍부한 프로그래머들은 shell script 를 능숙하게 작성하기도 하지만, 이런 경우가 아니라면 익숙한 프로그래밍 언어를 사용하는 것이 유리하고, 유지보수를 고려하면 개발팀의 전체적인 구성도 고려해야 한다.

shell script는 파일을 다루는 것에 매우 강력하지만, 파일이름에 escape sequence 가 포함되어 있거나, 다른 locale 로 저장된 파일이름 따위가 있다면, 그 처리가 쉽지는 않다.

포인터 연산이 많은 분야는 Java 가 특히 불리하다는 것이 일반적인 생각이다.

Java 의 성능에 대한 오해는 JVM 을 기동시키는 비용에서 비롯되었을 가능성이 높기는 하다.

Java 가 포인터를 지원하지 않지만, System.arraycopy() 따위로 코드를 최적화하는 경우 성능 측면에서 70 ~ 80% 수준까지 따라갈 수 있고, 그 정도는 대부분 무시할 수 있는 수준일 가능성이 높다.

System.arraycopy() 따위는 압축이나 이미지 처리와 같은 분야에서 사용되고, 이런 것들은 라이브러리를 작성할 때 사용하기 때문에, 이런 코드를 직접 사용할 일은 거의 없다.

적절한 third party 라이브러리를 찾을 수 없을 때는 예외로 한다.

4. shell script 나 procedure 는 필요 없는 것일까?

간단한 작업은 shell script 나 procedure 가 편리한 경우가 많다.

임기응변으로 대응해야 하는 상황이라면, shell script 나 procedure 외에 다른 대안이 없다.

작업시간이 정해져 있는 batch 작업중에, 예상치 못했던 데이타 같은 것이 발견되었다면, shell script 나 procedure 가 유일한 방법일 가능성이 높다.

공학적 관점과 어울리지 않고, 그런 일이 일어나지 않도록 사전에 충분히 대비를 해야겠지만, 비상상황은 발생하기 마련이다.

일회성 작업의 경우에도 shell script 나 procedure 를 많이 사용한다.

1개의 작업을 수행한 후에 그 결과를 확인하고 다음 작업을 수행하는 방식이라면, 다른 방법이 없을 가능성이 높다.

제목

첨부파일