오라클 옵티마이저 통계 수집 실무 가이드 – 자동 수집 vs 수동 수집, 무엇이 더 좋을까?

Posted by heoncode
2025. 5. 16. 17:31 오라클 실무 쿼리 튜닝
728x90
반응형
SMALL

오라클 데이터베이스에서 SQL 성능을 결정짓는 핵심 요소 중 하나는 바로 옵티마이저입니다. 이 옵티마이저가 쿼리 실행 계획을 수립할 때 가장 중요한 기준이 되는 것이 바로 통계 정보입니다. 그런데 이 통계를 언제, 어떻게 수집해야 하는지에 대한 기준이 명확하지 않아 실무에서 혼란을 겪는 경우가 많습니다. 이번 글에서는 통계 수집의 개념과 자동 수집 vs 수동 수집의 차이, 실무에서의 적용 방식을 정리합니다.

통계 정보란?

통계 정보는 테이블의 데이터 양, 컬럼의 값 분포, 인덱스 상태 등을 바탕으로 옵티마이저가 실행 계획을 최적화하는 데 사용하는 자료입니다. 대표적인 항목으로는 테이블의 ROW 수, 블록 수, 컬럼의 DISTINCT 수, NULL 비율 등이 있으며, 이 정보는 DBMS_STATS 패키지를 통해 수집됩니다.

자동 통계 수집의 특징

오라클 10g 이후부터는 매일 밤 10시~오전 2시 사이에 자동 통계 수집 작업이 실행되도록 설정되어 있습니다. 기본적으로는 GATHER_STATS_JOB이라는 잡이 이를 담당하며, 새로 생성된 테이블이나 변경된 데이터가 많은 테이블 위주로 통계를 수집합니다.

장점:

  • 별도의 관리 없이 자동 실행
  • 전체 테이블이 아닌 필요한 부분만 수집
  • 유지보수 부담이 적음

단점:

  • 예측이 어려운 시점에 수집될 수 있음
  • 중요한 배치 직후 통계가 반영되지 않을 수 있음
  • 테이블에 따라 과소 또는 과잉 수집이 발생할 수 있음

수동 통계 수집의 필요성

데이터가 급격히 바뀌거나, 대규모 배치 이후에 성능 문제가 발생하는 경우에는 자동 수집을 기다리는 것이 아니라 수동으로 즉시 통계를 갱신하는 것이 안전합니다. 이럴 때는 DBMS_STATS.GATHER_TABLE_STATSGATHER_SCHEMA_STATS 프로시저를 활용합니다.

예시:

BEGIN
    DBMS_STATS.GATHER_TABLE_STATS(
        ownname => 'SCOTT',
        tabname => 'EMP',
        cascade => TRUE
    );
END;

이처럼 수동 수집은 제어 가능성이 높고, 성능 이슈가 발생하기 전 사전에 대비할 수 있다는 장점이 있습니다.

실무 적용 팁

  • 대규모 데이터 변경 직후에는 수동 수집을 반드시 수행하는 것이 안전합니다.
  • 배치 작업이 정기적으로 이뤄지는 시스템이라면, 배치 이후 수동 수집을 자동화된 스크립트로 포함하는 것이 좋습니다.
  • 통계 수집 후 성능 저하가 발생할 경우에는 이전 통계를 롤백할 수 있도록 DBMS_STATS의 EXPORT/IMPORT 기능을 사용해 백업해두는 습관도 필요합니다.

예시 (통계 백업 및 복원):

-- 백업
EXEC DBMS_STATS.EXPORT_TABLE_STATS('SCOTT', 'EMP', 'STAT_BACKUP');

-- 복원
EXEC DBMS_STATS.IMPORT_TABLE_STATS('SCOTT', 'EMP', 'STAT_BACKUP');

결론

자동 수집은 편리하지만 예외 상황에 항상 대응할 수는 없습니다. 실무에서는 자동 수집을 기본으로 하되, 중요한 시점이나 대량 변경 이후에는 수동 수집을 병행해야 안정적인 SQL 성능을 유지할 수 있습니다. 옵티마이저의 의사결정을 믿기 위해서는 우리가 통계 수집이라는 재료를 제대로 공급해줘야 한다는 점을 기억해야 합니다.

#오라클 #SQL튜닝 #옵티마이저 #통계수집 #DBMS_STATS #실무팁 #성능최적화

728x90
반응형
LIST

서브쿼리 성능 최적화 – 서브쿼리에서 성능을 끌어올리는 5가지 방법

Posted by heoncode
2025. 4. 16. 09:12 오라클 실무 쿼리 튜닝
728x90
반응형
SMALL

서브쿼리는 SQL에서 매우 유용하게 쓰이는 기능이지만, 잘못 사용하면 전체 쿼리 성능을 크게 떨어뜨릴 수 있습니다. 특히 실무 환경에서는 서브쿼리가 느려지는 원인을 정확히 파악하고, 효율적으로 개선하는 것이 중요합니다. 이 글에서는 서브쿼리를 성능 저하 없이 사용하는 실전 최적화 팁 5가지를 소개합니다.

1. 불필요한 서브쿼리는 조인으로 대체

단순히 조건 확인용으로 쓰이는 서브쿼리는 조인으로 바꾸는 것이 더 나은 경우가 많습니다. 서브쿼리가 테이블을 여러 번 조회할 가능성이 있기 때문에, 조인으로 바꾸면 물리적 접근 횟수를 줄일 수 있습니다.

예시:

SELECT E.ename, D.dname
FROM emp E
JOIN dept D ON E.deptno = D.deptno
WHERE D.loc = 'DALLAS';

이와 같은 방식으로 WHERE 절 서브쿼리를 JOIN으로 바꾸면 쿼리 최적화에 유리해집니다.

2. 스칼라 서브쿼리는 주의해서 사용

SELECT 절에서 서브쿼리를 쓰는 경우, 특히 테이블 로우마다 반복 실행되기 때문에 성능 저하의 주범이 될 수 있습니다. 이런 경우도 조인으로 바꾸는 게 훨씬 효율적입니다.

비효율적인 예:

SELECT ename,
       (SELECT dname FROM dept D WHERE D.deptno = E.deptno) AS dname
FROM emp E;

대체 가능한 방식:

SELECT E.ename, D.dname
FROM emp E
JOIN dept D ON E.deptno = D.deptno;

3. EXISTS vs IN – 상황에 따라 구분

서브쿼리 안에서 IN을 사용하는 경우와 EXISTS를 사용하는 경우는 성능 차이가 발생할 수 있습니다. 일반적으로 EXISTS는 조건을 만족하는 첫 행만 찾으면 종료되므로 더 빠르게 처리됩니다.

-- EXISTS 예시
SELECT ename
FROM emp E
WHERE EXISTS (SELECT 1 FROM dept D WHERE D.deptno = E.deptno AND D.loc = 'DALLAS');

-- IN 예시
SELECT ename
FROM emp
WHERE deptno IN (SELECT deptno FROM dept WHERE loc = 'DALLAS');

두 방식 모두 테스트 후 더 빠른 쪽을 선택하는 것이 좋습니다.

4. 서브쿼리 결과를 뷰 또는 인라인 뷰로 활용

복잡한 서브쿼리를 여러 번 사용할 경우, 해당 서브쿼리를 뷰로 분리하거나 인라인 뷰로 활용하는 것이 성능을 개선하는 데 도움이 됩니다.

SELECT E.ename, V.dname
FROM emp E
JOIN (SELECT deptno, dname FROM dept WHERE loc = 'DALLAS') V
  ON E.deptno = V.deptno;

이렇게 하면 서브쿼리 결과를 캐시처럼 활용할 수 있어 반복 실행을 방지할 수 있습니다.

5. 서브쿼리 내부에도 인덱스가 필요하다

서브쿼리가 아무리 작아도, 내부에서 WHERE, JOIN, IN 등을 사용하는 경우 해당 조건 컬럼에 인덱스가 없다면 성능 병목이 생깁니다. 서브쿼리라고 해서 옵티마이저가 무시하지 않기 때문에, 내부 테이블의 통계 정보 및 인덱스 구성도 반드시 확인해야 합니다.


서브쿼리는 무조건 나쁘다는 오해가 많지만, 목적과 상황에 맞게 작성하면 충분히 강력한 도구가 됩니다. 위의 팁들을 실제 쿼리에 적용해보고, 옵티마이저 실행 계획을 분석해 보며 성능 개선 효과를 확인해보시기 바랍니다.


#서브쿼리 #SQL성능최적화 #오라클튜닝 #서브쿼리조인 #EXISTSvsIN #인라인뷰

728x90
반응형
LIST

SELECT COUNT(*)가 느릴 때 – 성능 개선 전략과 대안

Posted by heoncode
2025. 4. 15. 12:00 오라클 실무 쿼리 튜닝
728x90
반응형
SMALL

오라클에서 SELECT COUNT(*)는 가장 단순한 집계 함수 중 하나입니다. 하지만 실무에서는 이 간단한 쿼리조차 예상보다 오래 걸리는 경우가 많습니다. 특히 대용량 테이블을 대상으로 할 때 COUNT가 심각하게 느려진다면, 단순히 "집계 함수니까 느리다"는 설명만으로는 부족합니다.

이번 글에서는 SELECT COUNT(*)가 느려지는 원인을 분석하고, 이를 개선할 수 있는 실질적인 전략들을 정리합니다.

1. COUNT(*)는 인덱스를 사용할까?

많은 개발자들이 SELECT COUNT(*)가 자동으로 인덱스를 사용할 것이라 생각하지만, 조건절이 없다면 테이블 전체를 스캔하게 됩니다. 오라클은 COUNT를 계산하기 위해 반드시 모든 행을 확인해야 하며, 조건이 없다면 풀 테이블 스캔(Full Table Scan)이 수행됩니다.

조건이 있는 경우, 해당 조건에 적절한 인덱스가 존재한다면 옵티마이저가 인덱스를 사용할 수도 있습니다.

예시:

SELECT COUNT(*) FROM EMP WHERE DEPTNO = 10;

위 쿼리는 DEPTNO 컬럼에 인덱스가 존재한다면 INDEX RANGE SCAN으로 수행될 수 있습니다.

2. COUNT가 느린 주요 원인

다음은 실무에서 자주 발생하는 COUNT 성능 저하 원인입니다:

  • 조건이 없어 전체 테이블을 스캔하는 경우
  • 대용량 테이블에 파티션이 없거나 적절하지 않은 경우
  • 블록 내 저장된 행 수가 불균형하거나, CHAINED ROW가 많은 경우
  • VIEW나 서브쿼리 내부의 조인 비용이 높을 경우
  • DISTINCT, GROUP BY 등이 함께 쓰이는 경우

단순 COUNT일지라도 내부적으로 의외로 복잡한 연산 경로를 탈 수 있습니다. 실행 계획(EXPLAIN PLAN) 확인이 필요합니다.

3. 성능 개선 전략

✅ 조건을 최대한 활용하라

WHERE 절이 없는 COUNT는 테이블 전체를 스캔하기 때문에 매우 느립니다. 가능한 조건을 활용해 인덱스를 탈 수 있도록 쿼리를 구성하는 것이 좋습니다.

예시:

SELECT COUNT(*) FROM LOG_TABLE WHERE CREATE_DT >= SYSDATE - 1;

조건이 있다면 해당 컬럼에 인덱스를 추가하여 효율적인 COUNT가 가능합니다.

✅ 파티션 테이블 도입

대용량 로그성 테이블에서는 파티셔닝(partitioning)이 COUNT 성능에 큰 영향을 미칩니다. 예를 들어 일자별 RANGE 파티션을 구성하면, 최근 1일치 데이터에 대한 COUNT를 빠르게 조회할 수 있습니다.

✅ COUNT 대신 메타데이터 활용

전체 행 수만 필요하다면 DBA_TABLES, ALL_TABLES, USER_TABLESNUM_ROWS 컬럼을 사용할 수 있습니다. 단, 통계 정보가 최신이어야 정확합니다.

예시:

SELECT NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'EMP';

이 값은 DBMS_STATS.GATHER_TABLE_STATS 수행 시 갱신됩니다.

✅ COUNT 결과 캐싱 고려

변경이 자주 없는 테이블이나 집계용 로그 테이블의 경우, COUNT 결과를 별도 컬럼이나 캐시 테이블에 저장해두고 주기적으로 갱신하는 전략도 있습니다. 특히 대시보드 등에서 반복적으로 COUNT 쿼리가 실행된다면 효과적입니다.


마무리하며

SELECT COUNT(*)는 단순하지만, 그만큼 무심코 쓰기 쉬운 쿼리입니다. 특히 대용량 테이블에서는 성능 이슈로 이어지기 쉬우므로, COUNT 쿼리를 쓸 때도 항상 실행 계획을 점검하고 조건, 인덱스, 파티션 여부 등을 함께 고려해야 합니다.

이전에 작성한 INDEX가 안 잡힐 때 확인할 조건 글과 함께 참고하면, 쿼리의 성능 문제를 보다 체계적으로 점검할 수 있습니다.

#오라클 #SQL튜닝 #카운트성능 #대용량테이블 #파티셔닝 #DBMS_STATS #실무팁

728x90
반응형
LIST

인라인 뷰 vs 테이블 조인 – 같은 결과, 다른 처리 방식

Posted by heoncode
2025. 4. 14. 09:11 오라클 실무 쿼리 튜닝
728x90
반응형
SMALL

SQL을 작성하다 보면 인라인 뷰테이블 조인 중 어떤 방식이 더 좋은지 고민하게 되는 경우가 많습니다. 겉으로는 같은 결과를 도출할 수 있어 보이지만, 실제로는 처리 방식이나 성능 측면에서 차이를 보일 수 있습니다. 이번 글에서는 인라인 뷰와 테이블 조인의 차이점과 실무에서 선택 기준을 어떻게 잡아야 하는지 살펴보겠습니다.

인라인 뷰란?

인라인 뷰는 FROM 절 안에 작성된 서브쿼리로, 마치 임시 테이블처럼 작동합니다. 복잡한 데이터를 미리 가공한 후, 이를 다시 외부 쿼리에서 조인하거나 필터링할 수 있게 해줍니다.

예시:

SELECT emp.empno, emp.ename, dept.dept_name
FROM (
    SELECT * FROM emp WHERE sal > 3000
) emp
JOIN dept ON emp.deptno = dept.deptno;

위 쿼리는 먼저 급여가 3000을 초과하는 직원만 추출한 다음, 해당 결과를 기반으로 부서 테이블과 조인합니다. emp는 인라인 뷰로 사용되고 있습니다.

테이블 조인 방식

테이블 조인은 일반적으로 여러 테이블을 직접 JOIN하여 필요한 데이터를 한 번에 조회하는 방식입니다. 조건이 단순하거나 중간 가공이 필요 없는 경우에 사용됩니다.

예시:

SELECT e.empno, e.ename, d.dept_name
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.sal > 3000;

이 방식은 별도의 가공 없이 바로 조건을 걸고, 조인을 수행합니다. 데이터 양이 많지 않거나 단순한 경우, 옵티마이저가 효율적으로 처리해줄 가능성이 높습니다.

어떤 차이가 있을까?

두 방식은 결과적으로 동일한 데이터를 반환할 수 있지만, 옵티마이저의 처리 방식에 따라 성능에 차이를 보일 수 있습니다. 다음은 주요 차이점입니다:

  • 옵티마이저 최적화 수준: 인라인 뷰는 독립적으로 처리되는 경우가 많아 옵티마이저가 전체 쿼리의 실행 계획을 유연하게 조정하지 못하는 경우가 있습니다.
  • 필터 순서 제어: 인라인 뷰 내에서는 WHERE 절이 먼저 처리되므로, 복잡한 필터링이나 가공을 먼저 수행하고 조인을 하게 됩니다.
  • 읽어야 할 데이터 양: 조건에 따라 먼저 필터링을 해서 줄일 수 있다면 인라인 뷰가 더 효율적일 수 있지만, 그렇지 않으면 오히려 중복 연산이 될 수도 있습니다.

실무에서의 선택 기준

  1. 필터링 우선 여부: 먼저 데이터를 가공해야만 하는 경우 → 인라인 뷰
  2. 단순 조건 조합일 경우: 성능을 위해 옵티마이저에 맡기고 싶은 경우 → 테이블 조인
  3. 읽어야 할 데이터 양이 많은 경우: 인라인 뷰가 오히려 느려질 수 있음 → 테이블 조인 권장
  4. 복잡한 가공이나 집계를 별도로 해야 하는 경우: 인라인 뷰 활용

결론

인라인 뷰와 테이블 조인은 단순히 문법 차이가 아닌, 옵티마이저의 쿼리 처리 방식에 영향을 줍니다. 따라서 동일한 결과를 반환한다고 해서 성능도 같을 거라는 가정은 위험합니다. 실무에서는 실제 실행 계획을 확인하면서, 상황에 맞는 방식을 선택하는 것이 중요합니다.


#SQL튜닝 #인라인뷰 #테이블조인 #SQL성능 #서브쿼리 #오라클쿼리팁 #오라클튜닝

728x90
반응형
LIST