Link Search Menu Expand Document

결합

기능적 관점으로 구분하는 결합의 종류

크로스 결합 - 모든 결합의 모체

  • 데카르트 곱이라고 불리는 연산으로, 2개 테이블의 레코드에서 가능한 모든 조합을 구하는 연산
  • 크로스 결합이 실무에서 사용되지 않는 이유
    • 이러한 결과가 필요한 경우가 없음
    • 비용이 매우 많이 드는 연산
  • 실수로 사용한 크로스 결합
    SELECT *
    FROM Employees, Departments;
    
    • 결합 조건이 없으므로 DBMS는 두 개의 테이블에서 나올 수 있는 모든 조합을 생성
    • 이러한 실수를 막으려면 표준 SQL에 맞게 결합 구문을 사용하는 것이 좋음

내부 결합 - 왜 ‘내부’라는 말을 사용할까?

  • 내부는 ‘데카르트 곱의 부분 집합’이라는 의미
  • 기능적으로 상관 서브쿼리를 사용해 대체 가능한 경우가 많음
    SELECT E.emp_id, E.emp_name, E.dept_id,
         (SELECT D.dept_name
            FROM Departments D
           WHERE E.dept_id = D.dept_id) AS dept_name
    FROM Employees E;
    
  • 내부 결합과 상관 서브쿼리 중 내부 결합을 사용하는 것이 좋음
    • 상관 서브쿼리를 이용하면 결과 레코드 수만큼 상관 서브쿼리를 실행하므로 내부 결합보다 비용이 높음

외부 결합 - 왜 ‘외부’라는 말을 사용할까?

  • 내부 결합과 외부 결합은 배타적인 연산
  • 외부 결합은 ‘데카르트 곱의 부분 집합’이 아니라는 읨
  • 외부 결합의 종류
    • 왼쪽 외부 결합
    • 오른쪽 외부 결합
    • 완전 외부 결합
  • 마스터 테이블 쪽에만 존재하는 키가 있을 때 해당 키를 제거하지 않고 결과에 남김

내부 결합과 외부 결합의 차이

  • 외부 결합은 마스터 테이블의 정보를 모두 보존하기 위해 NULL을 생성
  • 크로스 결합과 내부 결합은 NULL을 생성하지 않음

자기 결합 - ‘자기’란 누구일까?

  • 자기 자신과 결합하는 연산으로 같은 테이블 (또는 같은 뷰)을 사용해 결합
  • 같은 테이블에 별칭을 붙여 마치 다른 테이블인 것처럼 취급

결합 알고리즘과 성능

  • 옵티마이저가 어떤 결합 알고리즘을 선택할지 여부는 데이터 크기 또는 결합 키의 분산에 의존

Nested Loops

Nested Loops

  1. 결합 대상 테이블(Table_A, driving table or outer table)에서 레코드를 하나씩 반복해가며 스캔
  2. Driving table의 레코드 하나마다 내부 테이블(Table_B, Inner table)의 레코드를 하나씩 스캔해서 결합 조건에 맞으면 리턴
  3. 이러한 작동을 Driving Table의 모든 레코드에 반복
  • 특징
    • 접근하는 레코드 수는 결합 대상인 두 테이블 레코드 수의 곱
    • 한 번의 단계에서 처리하는 레코드 수가 적으므로 Hash 또는 Sort Merge에 비해 메모리 소비가 적음
    • 모든 DBMS에서 지원
    • 구동 테이블이 작을수록 Nested Loops의 성능이 좋음
  • 구동 테이블의 중요성
    • 내부 테이블의 결합 키 필드에 인덱스가 존재할 경우 내부 테이블의 반복을 줄일 수 있음
    • 구동 테이블이 작고 내부 테이블의 결합 키에 인덱스가 존재하는 것이 SQL 튜닝의 기본
  • Nested Loops의 단점
    • 결합 키가 내부 테이블에 대해 유일하지 않은 경우 반복이 발생하므로 성능이 낮아짐
      • 이러한 경우 구동 테이블로 큰 테이블을 선택할 수 있음
      • 내부 테이블에 대한 구동 테이블의 접근이 기본 키로 수행되므로 항상 하나의 레코드로 접근하는 것이 보장됨

Hash

Hash Join

  • 결합 과정
    1. 작은 테이블을 스캔하고 결합 키에 해시함수를 적용해서 해시값으로 반환
    2. 큰 테이블을 스캔하고 결합 키가 해시값에 존재하는지 확인
  • 특징
    • 결합 테이블로부터 해시 테이블을 만들어서 활용하므로, Nested Loops에 비해 메모리를 크게 소모
    • 메모리가 부족하면 저장소를 사용하므로 지연 발생
    • 출력되는 해시값은 입력값의 순서를 알지 못하므로 등치 결합에만 사용할 수 있음
  • Hash가 유용한 경우
    • Nested Loops에서 적절한 구동 테이블(상대적으로 충분히 작은 테이블)이 존재하지 않는 경우
    • 구동 테이블로 사용할만한 작은 테이블은 있지만, 내부 테이블에서 히트되는 레코드 수가 너무 많은 경우
    • Nested Loops의 내부 테이블에 인덱스가 존재하지 않는 (또는 여러 가지 사정에 의해 인덱스를 추가할 수 없는) 경우
  • 주의할 사항
    • 초기에 해시 테이블을 만들어야 하므로 Nested Loops에 비해 소비하는 메모리 양이 많음
      • OLTP(Online Transaction Processing) 처리를 할 때 사용하면 안됨
      • 야간 배치 또는 BI(Business Intelligence)/DWH(Data Warehouse)와 같은 시스템에 한해 사용
    • 양쪽 테이블의 레코드를 전부 읽어야 하므로 풀 스캔에 걸리는 시간 고려

Sort Merge

  • 결합 대상 테이블들을 각각 결합 키로 정렬하고 일치하는 결합 키를 찾으면 결합

  • 특징
    • 대상 테이블을 모두 정렬하므로 Nested Loops보다 많은 메모리를 소비
    • 동치 결합 뿐만 아니라 부등호를 사용한 결합에도 사용할 수 있음
    • 테이블이 결합 키로 정렬되어 있다면 정렬을 생략할 수 있지만 이는 구현 의존적
    • 한 쪽 테이블을 모두 스캔한 시점에 결합을 완료함
  • Sort Merge가 유용한 경우
    • 테이블 정렬을 생략할 수 있는 경우에만 고려

의도하지 않은 크로스 결합

  • 삼각 결합에서 결합 조건이 존재하지 않는 두 테이블을 먼저 결합할 경우 크로스 결합이 발생
  • 비교적 큰 테이블끼리 크로스 결합을 할 경우 성능 문제 발생
  • 크로스 결합이 발생하는 두 테이블의 결합 조건을 설정할 수 있고, 그 결합 조건이 결과에 아무런 영향도 주지 않을 경우 크로스 결합 회피 가능

결합이 느리다면

상황에 따른 최적의 결합 알고리즘

이름장점단점
Nested Loops‘작은 구동 테이블’ + ‘내부 테이블의 인덱스’라는 조건이 있다면 굉장히 빠름
메모리 또는 디스크 소비가 적으므로 OLTP에 적합
비동기 결합에서도 사용 가능
대규모 테이블들의 결합에는 부적합
내부 테이블의 인덱스가 사용되지 않거나, 내부 테이블의 선택률이 높으면 느리다
Hash대규모 테이블들을 결합할 때 적합메모리 소비량이 큰 OLTP에는 부적합
메모리 부족이 일어나면 TEMP 탈락 발생
등가 결합에서만 사용 가능
Sort Merge대규모 테이블들을 결합할 때 적합
비등가 결합에서도 사용 가능
메모리 소비량이 큰 OLTP에는 부적합
메모리 부족이 일어나면 TEMP 탈락 발생
데이터가 정렬되어 있지 않다면 비효율적
  • 결합 대상 레코드 수에 따른 최적의 결합 알고리즘
    • 소규모 - 소규모: 어떤 알고리즘을 사용해도 성능 차이가 크지 않음
    • 소규모 - 대규모: 대규모 테이블의 결합 키에 인덱스를 만들어 Nested Loops를 사용. 하지만 내부 테이블의 결합 대상 레코드가 너무 많다면 구동 테이블과 내부 테이블을 바꾸거나 Hash 사용
    • 대규모 - 대규모: Hash 사용. 처음부터 결합 키로 정렬이 되어 있는 상태라면 Sort Merge 사용

실행 계획 제어

  • RDB에서는 원칙적으로 통계 정보를 바탕으로 옵티마이저가 실행 계획을 자동으로 수립
  • 실행 계획을 사용자가 제어할 때의 리스크
    • 데이터 양과 카디널리티는 데이터베이스를 운용하는 동안 계속 바뀌므로 어떤 시점에는 적절했던 실행 계획이 또 다른 시점에는 적절하지 않을 수 있음
    • 따라서, 사용자가 실행 계획을 제어할 때는 이러한 위험을 충분히 검토하고, 시스템의 미래 상황도 예측해서 적절한 실행 계획을 선택해야 함

흔들리는 실행 계획

  • 데이터 양의 증가 등에 따라 통계 정보가 변했을 때 일정한 역치를 넘으면 ‘옵티마이저의 실패’가 발생할 수 있음
  • SQL 성능의 변동 위험을 줄이려면 되도록 결합을 피해야 함