ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MySQL] 인덱스 > 클러스터링 인덱스
    DevBook 2025. 3. 26. 00:16
    • MySQL 서버에서 클러스터링클러스터링 키를 사용해 테이블의 레코드를 비슷한 것들끼리 묶어서 저장하는 형태로 구현됨
      • 주로 비슷한 값들을 동시에 조회하는 경우가 많다는 점에 착안한 것
    • MySQL에서 클러스터링 인덱스는 InnoDB 스토리지 엔진에서만 지원

     

    클러스터링 인덱스

    • 테이블의 PK에 대해서만 적용되는 내용
    • 즉, PK 값이 비슷한 레코드끼리 묶어서 저장하는 것을 '클러스터링 인덱스'라고 표현함
    • 중요한 것은 PK 값에 의해 레코드의 저장 위치가 결정된다는 것
      • PK 값이 변경되면 그 레코드의 물리적인 저장 위치가 바뀌어야 한다는 것을 의미하기도 함
      • 인덱스 알고리즘이라기보다 테이블 레코드의 저장 방식이라고 볼 수 있음
        • 클러스터링 인덱스와 클러스터링 테이블은 동의어로 사용되기도 함
        • 클러스터링의 기준이 되는 PK를 '클러스터링 키'라고도 표현함
    • InnoDB와 같이 항상 클러스터링 인덱스로 저장되는 테이블은 PK 기반의 검색이 매우 빠르며, 대신 레코드의 저장이나 PK의 변경이 상대적으로 느림

     

    Q. 세컨더리 인덱스와의 차이점?

    • 인덱스를 역할 기준으로 분류하면, 프라이머리 인덱스와 세컨더리 인덱스로 구분 가능
    • 클러스터링 인덱스의 리프 노드에는 레코드의 모든 컬럼이 같이 저장돼 있음
    • 클러스터링 테이블은 그 자체가 하나의 거대한 인덱스 구조로 관리되는 것

     

    Q. PK가 없는 InnoDB 테이블은 어떻게 클러스터링 테이블로 구성될까? (클러스터링 키로 무엇을 사용할까)

    • PK가 없는 경우 InnoDB 스토리지 엔진이 다음 우선순위대로 PK를 대체할 컬럼을 선택
    • 클러스터링 키 선택 우선순위
      1. PK가 있으면 기본적으로 PK를 클러스터링 키로 선택
      2. NOT NULL 옵션의 유닉크 인덱스(Unique Index) 중 첫 번째 인덱스를 클러스터링 키로 선택
      3. 자동으로 유니크한 값을 가지도록 증가되는 컬럼을 내부적으로 추가한 후, 클러스터링 키로 선택
        • 적절한 클러스터링 키 후보를 찾지 못하는 경우 내부적으로 레코드의 일련번호 컬럼을 생성함
        • 이렇게 자동으로 추가된 PK는 사용자에게 노출되지 않고, 쿼리 문장에서 명시적으로 사용할 수 없음
        • 즉, PK나 유니크한 인덱스가 전혀 없는 InnoDB 테이블에서는 아무 의미 없는 숫자 값으로 클러스터링되는 것

     

    세컨더리 인덱스에 미치는 영향

    • InnoDB 테이블(클러스터링 테이블)의 모든 세컨더리 인덱스는 해당 레코드가 저장된 주소가 아니라 PK 값을 저장하도록 구현돼 있음
      • Q. 세컨더리 인덱스가 실제 레코드가 저장된 주소를 가지고 있다면?
        • 클러스터링 키에 의해 레코드의 물리적인 저장 위치가 결정되기 때문에 클러스터링 키 값이 변경될 때마다 데이터 레코드의 주소가 변경되고 그때마다 해당 테이블의 모든 인덱스에 저장된 주소값을 변경해야 함
        • 이런 오버헤드를 제거하기 위해 PK 값을 저장하도록 구현돼 있음

     

    Q. MyISAM이나 MEMORY 테이블은?

    • MyISAM이나 MEMORY 테이블 같은 클러스터링되지 않은 테이블은 INSERT될 때 처음 저장된 공간에서 레코드가 절대 이동하지 않음
    • 따라서 데이터 레코드가 저장된 주소는 내부적인 ROWID(레코드 아이디) 역할을 함
    • PK나 세컨더리 인덱스의 각 키는 주소(ROWID)를 이용해 실제 데이터 레코드를 찾아옴
    • MyISAM이나 MEMORY 테이블에서는 PK와 세컨더리 인덱스는 구조적으로 아무런 차이가 없음

     

    예시)

    CREATE TABLE employees(
        emp_no INT NOT NULL,
        first_name VARCHAR(20) NOT NULL,
        PRIMARY KEY (emp_no),
        INDEX ix_firstname(first_name)
    );
    
    SELECT * FROM employees WHERE first_name='me';
    • MyISAM
      • ix_firstname 인덱스를 검색해서 레코드의 주소를 확인한 후, 레코드의 주소를 이용해 최종 레코드를 가져옴
    • InnoDB
      • ix_firstname 인덱스를 검색해 레코드의 PK 값 확인한 후, 프라이머리 키 인덱스 검색해 최종 레코드 가져옴
        • 세컨더리 인덱스 검색 --> PK 값 확인 --> 클러스터링 인덱스 검색

     

    클러스터링 인덱스의 장점과 단점

    참고)

    • 일반적으로 PK를 클러스터링 키로 선정하기 때문에 아래에 나오는 PK를 '클러스터링 키'라고 생각하면 됨
    • 일반적인 웹 서비스 같은 환경에서는 쓰기와 읽기 비율이 2:8 또는 1:9 정도이기 때문에 조금 느린 쓰기를 감수하고 빠른 읽기를 유지하는 것이 중요함

     

    장점 : 빠른 읽기

    • 클러스터링 키로 검색할 때 처리 성능이 매우 빠름
      • 특히, 클러스터링 키로 범위 검색하는 경우 매우 빠름 (클러스터링 키 기준으로 정렬되어 저장되어 있으니까)
    • 테이블의 모든 세컨더리 인덱스가 (실제 레코드의 주소가 아닌) PK를 가지고 있기 때문에 인덱스만으로 처리될 수 있는 경우가 많음

    단점 : 느린 쓰기

    • 테이블의 모든 세컨더리 인덱스가 클러스터링 키를 가지고 있기 때문에 클러스터링 키 값의 크기가 클 경우 전체적으로 인덱스의 크기가 커짐
    • 세컨더리 인덱스를 통해 검색할 때 PK로 다시 한번 검색해야 하므로 처리 성능이 느림
      • 세컨더리 인덱스는 PK를 가지고 있으므로 세컨더리 인덱스 검색 후 클러스터링 인덱스 검색해야 함
    • INSERT할 때 PK에 의해 레코드의 저장 위치가 결정되기 때문에 처리 성능이 느림
    • PK를 변경할 때 레코드를 DELETE하고 INSERT하는 작업이 필요하기 때문에 처리 성능이 느림

     

    클러스터링 테이블 사용 시 주의사항

    1) 클러스터링 인덱스 키의 크기

    • 클러스터링 테이블의 경우 모든 세컨더리 인덱스가 클러스터링 키 값을 포함
    • 따라서 클러스터링 키의 크기가 커지면 세컨더리 인덱스도 자동으로 크기가 커짐
    • 인덱스의 크기가 커질수록 같은 성능을 내기 위해 그만큼의 메모리가 더 필요해지므로 InnoDB 테이블의 클러스터링 키는 신중하게 선택해야 함
      • 인덱스의 크기가 커지면 하나의 Page 내 저장 가능한 인덱스 레코드 수가 줄어들게 되어 더 많은 Page를 메모리에 올려야 하기 때문

     

    2) PK는 AUTO-INCREMENT보다는 업무적인 컬럼으로 생성(가능한 경우)

    • PK는 클러스터링 키로 사용되기 때문에 PK에 의해 레코드의 물리적인 저장 위치가 결정
    • 즉, PK로 검색하는 경우(특히 범위로 많은 레코드를 검색하는 경우) 클러스터링되지 않은 테이블에 비해 매우 빠르게 처리될 수 있음
    • 대부분 검색에서 상당히 빈번하게 사용되는 것이 일반적이므로, 컬럼의 크기가 크더라도 업무적으로 해당 레코드를 대표할 수 있다면 해당 컬럼을 PK로 설정하는 것이 좋음 (의미있는 값을 PK로 설정해 검색 시 사용하기 위함)

     

    3) PK는 반드시 명시할 것

    • 가능하면 AUTO_INCREMENT 컬럼을 이용해 PK를 생성하는 것을 권장
    • InnoDB 테이블에서 PK를 정의하지 않으면 InnoDB 스토리지 엔진이 내부적으로 일련번호 컬럼을 추가해 해당 컬럼을 클러스터링 키로 사용함 (NOT NULL 조건의 유닉크 인덱스도 존재하지 않는 경우)
      • 이렇게 자동으로 추가된 컬럼을 사용자에게 보이지 않으므로 사용자가 접근할 수 없음
    • 즉, InnoDB 테이블에 PK를 설정하지 않는 경우와 AUTO_INCREMENT 컬럼을 생성해 PK로 설정하는 것이 결국 똑같다는 것
    • 그렇다면 사용자가 사용할 수 있는 값(AUTO_INCREMENT 값)을 PK로 설정하는 것이 오히려 낫다는 것
    • 추가) ROW 기반의 복제나 InnoDB 클러스터에서는 모든 테이블이 PK를 가져야만 정상적인 복제 성능을 보장하기도 하므로 PK는 꼭 생성하자

     

    4) AUTO-INCREMENT 컬럼을 인조 식별자로 사용할 경우

    • 여러 개의 컬럼이 복합으로 PK가 만들어지는 경우 PK의 크기가 길어질 때가 있음
    • PK 크기가 길어도 세컨더리 인덱스가 필요하지 않는 경우에는 그대로 PK를 사용하는 것이 좋음
    • 만약, 세컨더리 인덱스도 필요하고 PK의 크기도 길다면 AUTO_INCREMENT 컬럼을 추가해 이를 PK로 설정하면 됨
    • 이렇게 PK 대체를 위해 인위적으로 추가된 PK를 '인조 식별자(Surrogate key)'라고 함
      • 로그 테이블 같이 조회보다 INSERT 위주의 테이블들은 AUTO_INCREMENT를 사용한 인조 식별자를 PK로 설정하는 것이 성능 향상에 도움이 됨

    'DevBook' 카테고리의 다른 글

    [MySQL] 실행 계획 > Extra 컬럼 > 커버링 인덱스 (Using Index)  (0) 2025.03.26
    [MySQL] 인덱스  (0) 2025.03.25

    댓글

Designed by Tistory.