Post

<오라클 성능 고도화 원리와 해법1> Ch05-06 페이지 처리의 중요성

오라클 성능 고도화 원리와 해법1 - Ch05-06 페이지 처리의 중요성

클라이언트/서버 환경에서 대용량 데이터를 조회할 때는 커서를 닫지 않은 채 사용자 이벤트(스크를 바를 내리거나 ‘다음’ 버튼을 클릭하는 등)가 발생할 때마다 결과 집합을 Fetch 하도록 구현할 수 있었다. 하지만 웹 애플리케이션 환경에서는 커서를 계속 오픈한 채로 결과 집합을 핸들링할 수 없다. 즉, 데이터베이스와의 연결을 지속하지 않는 웹 환경이기 때문에 사용자가 다음 페이지를 요청하거나 스크롤바를 내릴 때마다 개별적인 SQL 문을 수행하는 방식으로 페이지 처리를 구현해야 한다. 그러다 보니 페이지 처리 구현 방식이 자주 이슈가 되곤 한다.

거짓말같지만 화면 페이지 처리를 아래와 같이 구현한 개발팀을 본 적이 있다. 국내에 잘 알려진 그룹웨어를 개발하는 회사였다.

1
2
3
4
5
6
7
void pagination(ResultSet rs, long pageNo, int pageSize) throws Exception {
    int i = 0;
    while (rs.next()) {
        if (++i > (pageNo - 1) * pageSize) printRow(rs);
        if (i == pageNo * pageSize) break;
    }
}

데이터를 계속 Fetch 하다가 출력해야 할 페이지(pageNo)에도 달하면 그때부터 printRow를 호출해 pagesize 개수만큼 로우를 출력하고, 그제야 Fetch를 멈춘다. C, JAVA 문법에 익숙하지 않은 독자들을 위해 PL/SQL로 변환하면 아래와 같다.

i := 0;
loop
    fetch c into l_record;
    exit when (c%notfound or (i = pageNo * pageSize));
    i := i + 1;
    if i > (pageNo - 1) * pageSize then
        printRow(l_record);
    end if;
end loop;

뒷 페이지로 이동할수록 엄청나게 많은 Fetch Call을 유발하게 될 것이고, 전반적으로 이런 스타일로 구현했다면 시스템에 얼마나 악영향을 끼칠지는 어렵지 않게 짐작할 수 있다.

FetchSize를 늘리면 도움이 될까? 뒷 페이지를 클릭할 때 발생하는 불필요한 Fetch Count 는 줄일 수 있겠지만 Array 버퍼에 쌓인 데이터양을 읽으면서 발생하는 CPU 리소스 낭비는 여전하고 메모리 사용량은 늘어날 것이며, 앞쪽 페이지를 출력할 때는 서버로부터 불필요하게 많은 로우를 가져와야 하므로 서버 및 네트워크 부하를 가중시켜 상황이 더 안 좋아질지도 모른다.

유일한 해결책은, 페이지 처리를 서버단에서 완료하고 최종적으로 출력할 레코드만 Fetch 하도록 프로그램을 고치는 것이다.

위와 같이 비효율적인 방식으로 페이지 처리를 구현하는 경우가 있는가 하면 시스템 전체적으로 아예 페이지 처리 없이 개발하는 예도 종종 볼 수 있다. 업무 요건 또는 짧은 개발 기간 때문이라고 하지만 대량의 결과 집합을 페이지 처리 없이 모두 출력하도록 구현했을 때 시스템 전반에 일으키는 부하는 실로 엄청나다. 페이지 처리를 하지 않았을 때 발생하는 부하요인을 요약하면 아래와 같다.

  1. 다량의 Fetch Call 발생(SQL *Net roundtrips to/from Client)
  2. 대량의 결과 집합을 클라이언트로 전송하면서 발생하는 네트워크 부하 (bytes sent via SQL *Net to client)
  3. 대량의 데이터 블록을 읽으면서 발생하는 I/O 부하
  4. AP 서버 및 웹 서버 리소스 사용량 증가

이렇게 여러 가지 부하를 일으키지만 정작 사용자는 앞쪽 일부 데이터만 읽고 업무 처리를 완료하는 경우가 대부분이며, 쿼리 자체 성능도 문제지만 시스템 전반에 걸쳐 불필요한 리소스를 낭비하게 되는 것이 더 큰 문제다. 이들 부하를 해소하는 열쇠는 페이지 처리에 있다.

  1. 페이지 단위로, 화면에서 필요한 만큼씩 Fetch Call
  2. 페이지 단위로, 화면에서 필요한 만큼씩 네트워크를 통해 전송
  3. 인덱스와 부분범위처리 원리를 이용해 각 페이지에 필요한 최소량만 I/O
  4. 데이터를 소량씩 나누어 전송하므로 AP • 웹 서버 리소스 사용량 최소화

결론적으로 말해, 조회할 데이터가 일정량 이상이고 수행빈도가 높다면 필수적으로 페이지 처리를 구현해야 한다.

페이지 처리를 하지 않는 이유를 물으면 대개 현업의 업무 요건 때문이라는 답변을 듣게 되는데, 수만~수십만 건에 이르는 데이터를 화면에서 스크롤해가며 분석하는 경우는 거의 없다. 대부분 엑셀로 저장하고 거기서 추가 가공 및 분석하려는 경우이므로 일반 조회 시에는 페이지 처리를 하고, 사용자가 필요할 때 만 ‘전체 조회’ 또는 ‘다운로드 받기’ 버튼을 클릭할 수 있도록 기능을 제공하는 것이 바람직하다.

페이지 처리를 하는 목적은, 1차적으로 데이터베이스 Call 횟수를 줄이고 네트워크를 통한 데이터 전송량을 최소화하는 데에 있지만, 서버 내에서의 처리 일량을 줄이는데(위에서 열거한 부하요인 중 3번)에도 있다. 따라서 그 목적에 맞게 구현해야 한다. 이를 테면, 조회 조건에 부합하는 100만 건 중 화면 출력에 필요한 최소 한 의 레코드 만 액세스하도록 I/O 효율을 고려해 구현해야 하는 것이다. 업무 특성에 따라 여러 가지 방식이 있는데, 성능을 고려한 효과적인 페이지 처리 기법에 대해서는 2권에서 자세히 다룬다.

This post is licensed under CC BY 4.0 by the author.