데이터베이스 성능 최적화는 모든 기업의 IT 시스템에서 중요한 요소입니다. 특히 실시간으로 많은 데이터를 처리하는 OLTP (Online Transaction Processing) 환경에서는 쿼리의 성능이 비즈니스에 큰 영향을 미칠 수 있습니다. 성능 최적화를 이루기 위해서는 데이터베이스에서 데이터를 효율적으로 조회하고 전송하는 방법이 필수적입니다. 이때 중요한 기법 중 하나가 부분범위 처리와 인덱스 스캔 효율화입니다. 이번 글에서는 이 두 가지 기법을 구체적으로 설명하고, 각각의 실제 적용 방법에 대해 다루겠습니다. 이를 통해 데이터베이스 성능을 향상시키고 쿼리 처리 속도를 최적화할 수 있는 방법을 배울 수 있을 것입니다.
3.2 부분범위 처리 활용
3.2.1 부분범위 처리란?
부분범위 처리는 데이터베이스가 쿼리 결과를 한 번에 모두 전송하지 않고, 일정량씩 나누어 전송하는 기법입니다. 이는 서버 자원을 효율적으로 사용하고, 클라이언트의 성능을 높이는 데 도움이 됩니다. 특히 OLTP 환경에서 부분범위 처리는 대규모 데이터를 빠르게 처리하는 데 필수적인 역할을 합니다.
예를 들어, 은행 시스템에서 사용자가 1년치의 거래 내역을 조회할 때, 데이터베이스는 사용자가 당장 필요로 하는 상위 100건의 데이터만 우선 전송할 수 있습니다. 이후 사용자가 추가 데이터를 요청하면 그때 추가적인 데이터를 전송하는 방식으로 자원을 절약하고 응답 속도를 높일 수 있습니다.
또 다른 예로, 전자상거래 사이트에서는 제품 리스트를 조회할 때, 사용자는 모든 제품을 다 보지 않고 상위 몇 페이지의 상품만 확인합니다. 이때 부분범위 처리 방식을 사용하여 전체 데이터를 한꺼번에 전송하지 않고, 사용자가 필요로 하는 일부 데이터만 우선 제공하는 것이 더 효율적입니다.
3.2.2 Array Size 조정 및 부분범위 처리
Array Size는 부분범위 처리에서 매우 중요한 요소입니다. 클라이언트가 한 번의 요청으로 서버에서 얼마나 많은 데이터를 가져올지를 결정하는 값입니다. 기본적으로 자바 프로그램에서는 Array Size의 기본값이 10으로 설정되어 있지만, 대규모 데이터를 다룰 때는 이 값을 조정하는 것이 성능 최적화에 도움이 됩니다.
Array Size가 작을 경우, 자주 Fetch Call이 발생하여 성능이 저하될 수 있습니다. 반면, Array Size가 지나치게 클 경우, 필요 없는 데이터를 많이 가져오게 되어 메모리와 네트워크 자원이 낭비하게 됩니다. 따라서 적절한 값을 설정하는 것이 중요합니다.
예를 들어, 뉴스 웹사이트에서 최신 기사를 조회할 때, Array Size를 10으로 설정하면 처음 10개의 기사가 먼저 화면에 나타나고, 사용자가 페이지를 스크롤할 때 추가적인 데이터를 가져오는 방식으로 처리할 수 있습니다. 이를 통해 불필요한 데이터 전송을 줄이고, 사용자에게 빠른 응답을 제공합니다.
추가 예로, 대규모 재고 데이터를 조회하는 시스템에서 사용자가 상위 100개의 상품만 조회할 경우, 전체 재고 데이터를 전송하는 대신 100개씩 나누어 데이터를 가져와 처리할 수 있습니다.
예시:
다음 자바 코드는 부분범위 처리를 적용한 예시입니다. 이 코드는 대용량 데이터를 효과적으로 처리하여, 처음 100개의 데이터만 화면에 출력합니다.
private void execute(Connection con) throws Exception {
Statement stmt = con.createStatement();
Result rs = stmt.executeQuery("SELECT name FROM big_table");
for (int i = 0; i < 100; i++) {
if (rs.next()) System.out.println(rs.getString(1));
}
rs.close();
stmt.close();
}
다음은 Array Size 값을 변경하여 부분범위 처리를 최적화하는 방법을 보여주는 코드입니다.
Statement stmt = con.createStatement();
stmt.setFetchSize(50); // Array Size를 50으로 설정
Result rs = stmt.executeQuery("SELECT * FROM big_table");
while (rs.next()) {
System.out.println(rs.getString(1));
}
이 코드는 처음 50개의 데이터를 가져오며, 필요한 데이터만 빠르게 처리하도록 설계되어 있습니다. 이를 통해 대용량 데이터 조회 시 성능을 최적화할 수 있습니다.
3.3 인덱스 스캔 효율화
3.3.1 인덱스 탐색
데이터베이스에서 인덱스는 테이블의 데이터를 효율적으로 조회하는 데 필수적인 도구입니다. 인덱스는 데이터를 정렬된 상태로 유지하며, 쿼리에서 특정 데이터를 찾을 때 전체 테이블을 스캔하는 대신 빠르게 해당 데이터를 찾아낼 수 있도록 도와줍니다.
인덱스 탐색은 수직적 탐색과 수평적 탐색으로 나뉩니다. 수직적 탐색은 인덱스 트리 구조에서 루트 블록에서 시작해 리프 노드로 내려가며 스캔하는 방식이며, 수평적 탐색은 리프 노드 내에서 필요한 데이터를 찾는 과정입니다.
잘 설계된 인덱스는 쿼리의 성능을 획기적으로 향상시킬 수 있습니다. 특히 대규모 데이터베이스에서 인덱스는 필수적입니다. 예를 들어, 도서관에서 책을 찾을 때, 책 제목의 첫 글자를 기준으로 책을 빠르게 찾는 방식과 유사합니다.
추가 예로, 고객의 주문 내역을 조회하는 시스템에서 특정 기간 동안의 주문 내역만 조회하는 경우 인덱스가 없다면 전체 테이블을 검색해야 하지만, 인덱스를 사용하면 해당 기간 내의 데이터만 빠르게 검색할 수 있습니다.
3.3.2 인덱스 스캔 효율성
인덱스 스캔의 효율성은 쿼리의 선행 컬럼이 어떻게 사용되느냐에 따라 크게 달라집니다. 선행 컬럼이 ‘=’ 조건으로 사용될 경우, 인덱스는 매우 효율적으로 작동할 수 있습니다. 하지만 BETWEEN, LIKE와 같은 범위 조건을 사용하거나, 선행 컬럼이 제외될 경우에는 성능이 저하될 수 있습니다.
SQL 트레이스 도구를 사용하여 인덱스 스캔의 효율성을 확인할 수 있습니다. 만약 비효율적인 인덱스 스캔이 발생한다면, 인덱스 구성을 재설계하거나 쿼리 자체를 최적화할 필요가 있습니다. 예를 들어, 범위 조건 대신 IN-List를 사용하는 방식으로 쿼리를 최적화할 수 있습니다.
또 다른 예로, 주문 시스템에서 사용자가 특정 제품의 가격 변동 기록을 조회할 때, 인덱스를 사용하지 않으면 전체 테이블을 스캔하게 됩니다. 하지만 인덱스를 적용하면 가격 변동 내역만 빠르게 조회할 수 있습니다.
3.3.3 액세스 조건과 필터 조건
쿼리에서 인덱스를 스캔하는 과정에서 조건절은 액세스 조건과 필터 조건으로 나뉩니다. 액세스 조건은 인덱스 스캔의 범위를 결정하며, 인덱스 탐색을 통해 스캔할 시작점과 끝점을 설정합니다. 필터 조건은 테이블에 접근해 데이터를 필터링할지를 결정하는 조건입니다.
효율적인 인덱스 스캔을 위해서는 필터 조건보다는 액세스 조건을 통해 대부분의 데이터를 필터링하는 것이 중요합니다. 이렇게 함으로써 테이블 접근을 최소화하고, 쿼리 성능을 최적화할 수 있습니다.
3.3.4 비교 연산자의 효율성
‘=’ 연산자는 인덱스 스캔에서 가장 효율적인 연산자입니다. 인덱스는 같은 값을 가진 레코드들이 군집되어 있기 때문에, ‘=’ 조건을 사용하면 해당 데이터에 빠르게 접근할 수 있습니다. 반면, BETWEEN이나 LIKE와 같은 범위 조건은 인덱스 스캔의 효율성을 떨어뜨릴 수 있습니다. 이러한 비효율을 해결하기 위해 BETWEEN 조건을 IN-List로 변경하는 것이 좋은 방법입니다.
예시:
다음 SQL 쿼리는 인덱스 선행 컬럼을 사용한 효율적인 데이터 조회 방법을 보여줍니다.
sql코드 복사SELECT 해당층, 평당가, 입력일, 해당동, 매물구분, 연사용일수, 중개업소코드
FROM 매물아파트매매
WHERE 아파트시세코드 = 'A01011350900056'
AND 평형 = '59'
AND 평형타입 = 'A'
AND 인터넷매물 BETWEEN '1' AND '3'
ORDER BY 입력일 DESC;
BETWEEN 대신 IN-List로 쿼리를 최적화한 경우는 다음과 같습니다.
sql코드 복사SELECT 해당층, 평당가, 입력일, 해당동, 매물구분, 연사용일수, 중개업소코드
FROM 매물아파트매매
WHERE 인터넷매물 IN ('1', '2', '3')
AND 아파트시세코드 = 'A01011350900056'
AND 평형 = '59'
AND 평형타입 = 'A'
ORDER BY 입력일 DESC;
또 다른 예로, LIKE 조건을 사용한 쿼리를 IN-List로 변경한 코드입니다.
sql코드 복사SELECT * FROM 고객정보
WHERE 이름 LIKE '김%'
AND 가입일 BETWEEN '2021-01-01' AND '2023-12-31';
이를 IN-List 방식으로 최적화한 쿼리입니다.
sql코드 복사SELECT * FROM 고객정보
WHERE 이름 IN ('김철수', '김영희', '김민수')
AND 가입일 BETWEEN '2021-01-01' AND '2023-12-31';
IN-List로 변경하면 LIKE와 BETWEEN 조건에서 발생할 수 있는 비효율을 줄일 수 있습니다.
결론
데이터베이스 성능을 최적화하기 위해서는 부분범위 처리와 인덱스 스캔 효율화와 같은 기법을 적극적으로 활용해야 합니다. 부분범위 처리를 통해 서버 자원을 효율적으로 사용하고, 사용자가 필요한 데이터만 빠르게 가져올 수 있습니다. 또한, 인덱스 스캔 효율화를 통해 쿼리 성능을 극대화할 수 있으며, 테이블 전체를 스캔하는 비효율을 피할 수 있습니다. 이 두 가지 기법은 OLTP 환경에서 필수적인 도구로, 데이터베이스의 응답 속도를 높이고 서버 자원을 절약하는 데 큰 도움을 줄 수 있습니다. 이를 통해 기업은 더 나은 성능을 제공하고, 데이터 처리에서 효율성을 극대화할 수 있습니다.