Week3 Index

인덱스

랜덤 I/O와 순차 I/O에 대해 설명해주세요

랜덤 I/O: 헤드가 디스크의 여러 위치로 빠르게 이동해야 하기 때문에 탐색 시간이 길어지고, 그 결과 성능이 저하됩니다. 비연속적인 위치에서 데이터를 읽거나 쓰는 방식입니다. 파일 시스템이나 데이터베이스에서 데이터가 디스크의 여러 위치에 흩어져 있을 때 주로 발생합니다.

  • 특징: 데이터를 읽고 쓰는 위치가 임의적이므로, 디스크의 헤드가 자주 움직여야 합니다. 특히 하드 디스크(HDD)에서 이 방식은 성능 저하를 초래할 수 있습니다. 디스크 헤드의 이동(시크 타임)이 많이 발생하고, 디스크 회전 대기 시간이 길어질 수 있기 때문입니다.

  • 장점: 특정 위치에 빠르게 접근하여 데이터를 처리할 수 있으므로, 소량의 데이터를 여러 곳에서 읽어야 하는 경우에 유리합니다.

  • 단점: 물리적 디스크의 특성상, I/O 성능이 낮아질 수 있습니다. 특히 하드 디스크(HDD)에서는 랜덤 접근이 비효율적입니다.

예시

  • 데이터베이스에서 인덱스를 통해 특정 레코드에 접근할 때

  • 파일 시스템에서 여러 파일을 동시에 읽거나 쓸 때

순차 I/O: 헤드가 디스크에서 연속적으로 데이터를 읽기 때문에 탐색 시간이 줄어들어 성능이 훨씬 빠릅니다.

순차 I/O는 연속적인 데이터 블록을 순서대로 읽거나 쓰는 방식입니다. 이는 디스크에 저장된 데이터가 연속적인 위치에 저장되어 있을 때 주로 발생합니다.

  • 특징: 데이터를 순차적으로 처리하므로, 디스크 헤드가 한 번에 긴 데이터 블록을 읽거나 쓸 수 있어 I/O 효율성이 높습니다. 특히 하드 디스크에서는 순차 I/O가 매우 빠르며, 디스크 회전과 헤드 이동이 최소화됩니다.

  • 장점: 대용량 데이터를 빠르게 처리할 수 있으며, 성능이 매우 우수합니다. 일반적인 HDD나 SSD 모두 순차 I/O에서 높은 성능을 보입니다.

  • 단점: 특정 위치에 있는 데이터를 즉시 접근하기보다는, 전체 데이터 흐름을 따라 읽거나 써야 하기 때문에 유연성이 떨어질 수 있습니다.

예시

  • 로그 파일에 데이터를 추가할 때 (로그는 주로 순차적으로 쓰여짐)

  • 대용량 파일을 연속적으로 읽을 때 (예: 비디오 파일)

인덱스가 뭔가요?

  • 인덱스는 데이터베이스에서 검색 성능을 높이기 위해 사용하는 자료 구조

  • 인덱스가 없으면, 테이블의 모든 데이터를 처음부터 끝까지 스캔하는 풀 스캔이 발생하지만, 인덱스를 사용하면 인덱스를 통해 필요한 레코드의 위치를 빠르게 조회

  • 인덱스는 주로 B-트리(B-tree) 구조로 구현되며, 이는 균형 잡힌 트리로서 삽입, 삭제, 검색 시 일정한 성능을 보장

  • 하지만 인덱스는 삽입, 수정, 삭제 작업 시 추가적인 오버헤드를 발생시킵니다. 인덱스는 항상 정렬된 상태를 유지해야 하므로, 레코드를 수정하거나 삭제할 때 인덱스도 함께 갱신

인덱스의 동작 방식에 대해서 설명해주세요

인덱스 테이블에 특정 컬럼들만 저장하고 해시 또는 B트리를 통해 빠르게 검색이 가능합니다.

B-트리

  • B-트리 구조로 구현. 균형 이진 트리와 유사하지만, 각 노드가 여러 개의 자식 노드를 가질 수 있는 구조

  • 데이터 삽입, 삭제, 검색 작업이 log(n)의 시간 복잡도

  • 내부 노드에는 해당 노드의 키 값과 함께 각 자식 노드로 이동하기 위한 포인터가 포함되어 있으며, 검색 대상이 내부 노드의 키 값에 맞는 경우에는 리프 노드까지 가지 않고도 데이터를 얻을 수 있습니다.

해시

해시 인덱스는 특정 값에 대해 해시 함수를 적용하여 데이터가 저장된 위치를 빠르게 찾는 방식입니다. 이 방식은 정확한 값을 검색할 때 매우 효율적이지만, 범위 검색에는 적합하지 않습니다.

  • 해시 값 계산: 해시 함수는 입력된 키 값을 기반으로 고유한 해시 값을 계산합니다.

  • 해시 버킷: 계산된 해시 값은 특정 버킷(bucket)에 매핑됩니다. 해당 버킷에 저장된 데이터를 읽어와 비교하여 원하는 데이터를 찾습니다.

어떤 기준으로 인덱스를 설정해야 할까요?

  • 유일성, 카디널리티, 검색 패턴, 데이터 수정 빈도 등을 종합적으로 고려

  • 카디널리티가 높은 컬럼: 인덱스는 주로 카디널리티가 높은 컬럼에 설정하는 것이 좋습니다. 카디널리티란 해당 컬럼에서 유일한 값의 개수를 의미

  • 자주 검색되는 컬럼: 인덱스는 검색 조건에 자주 사용되는 컬럼에 설정하는 것이 효율적입니다. WHERE 절에서 자주 사용되는 컬럼이나 JOIN에서 자주 쓰이는 컬럼에 인덱스를 걸면, 검색 속도를 크게 향상시킬 수 있습니다.

  • 삽입/수정/삭제 빈도 고려.

테이블에 인덱스를 많이 설정하면 좋을까요?

  • 너무 많은 인덱스를 설정하는 것은 성능에 부정적인 영향

  • 쓰기 작업의 성능 저하 (읽기 성능과 쓰기 성능 간의 트레이드 오프)

  • 인덱스 저장 공간

  • 쿼리 옵티마이저의 인덱스 선택 문제: 옵티마이저가 잘못된 인덱스를 선택할 가능성

커버링 인덱스(Covering Index)에 대해서 설명해주세요

  • 쿼리에서 필요한 모든 데이터를 인덱스 자체에서 조회할 수 있는 인덱스. 즉, 인덱스만으로 결과를 반환할 수 있는 경우.

  • 디스크 I/O 감소: 테이블의 데이터를 직접 읽는 대신, 인덱스만으로 데이터를 가져올 수 있으므로 디스크 I/O가 줄어들어 성능이 크게 향상

  • 성능 최적화

이 경우, 인덱스 idx_department_name이 department, id, name 열을 모두 포함하고 있기 때문에, 데이터베이스는 인덱스만으로도 필요한 모든 데이터를 가져올 수 있습니다. 즉, 테이블에 접근할 필요 없이 인덱스만으로 쿼리가 처리됩니다.

다중 컬럼 인덱스(복합 인덱스)에 대해서 설명해주세요

  • 두 개 이상의 컬럼을 하나의 인덱스로 묶어 한 번에 여러 조건을 기반으로 검색 성능을 최적화할 수 있는 인덱스

  • 컬럼의 순서에 따라 성능이 달라집니다. (예를 들어, (A, B, C)로 설정된 인덱스는 **첫 번째 컬럼(A)**에 기반한 검색에서만 성능 향상이 있습니다. 만약 WHERE B = ?와 같이 두 번째 컬럼만 검색할 경우에는 인덱스가 제대로 사용되지 않을 수 있습니다.) 선두 컬럼부터 차례대로 조건에 맞는 값을 찾아나가며, 따라서 쿼리의 조건이 선두 컬럼과 일치할 때에만 인덱스가 효과적으로 사용

  • 향상된 검색 성능

  • 특정 쿼리에 최적화: WHERE A = ? AND B = ?

  • 메모리 사용 증가

  • 삽입/삭제/수정 시 성능 저하

  • 쿼리 옵티마이저 선택 문제

B-, B+ 트리 인덱스에 대해 설명해주세요.

자가 균형 트리(Self-balancing Tree)로, 데이터베이스 인덱스에서 효율적인 검색, 삽입, 삭제를 위해 사용되는 트리 구조

B- 트리

  • B-Tree는 내부 노드와 리프 노드 모두에 데이터와 키 값을 저장

  • 내부 노드에서 원하는 데이터를 찾으면 리프 노드까지 탐색할 필요가 없음

  • 범위 검색에는 비효율적

B+ 트리

  • 모든 데이터를 리프 노드에만 저장하는 구조

  • 범위 검색에 유리 (순차 검색이 빠름)

  • 메모리 효율성: 내부 노드에 데이터를 저장하지 않기떄문에 더 많은 키를 저장할 수 있어서 트리의 깊이가 작음

Hash 인덱스에 대해 설명해주세요

  • 정확한 값 일치 검색에 매우 빠른 성능 하지만 범위 범색 불가능

  • 데이터를 정렬하지 않기 때문에, BETWEEN, >, < 같은 조건을 사용할 때 매우 비효율적

  • 체이닝 필요: 해시 함수는 서로 다른 입력 값이 동일한 해시 값을 가질 수 있는 충돌(Collision)이 발생할 수 있습니다.

chevron-right해시 충돌의 발생 원리hashtag

해시 함수는 입력 값을 일정한 범위 내의 값으로 변환하는 함수입니다. 해시 테이블의 크기는 유한하므로 해시 함수가 생성할 수 있는 해시 값의 수 역시 유한합니다. 반면, 입력 값은 매우 다양하고 무한한 경우가 있을 수 있기 때문에 서로 다른 두 입력 값이 같은 해시 값을 가질 수밖에 없습니다. 이때 해시 테이블에서 동일한 해시 값에 여러 입력 값이 할당되면서 충돌이 발생합니다.

  • 해시 충돌 해결 방법

    • 체이닝: 동일한 해시 값을 가진 데이터를 연결 리스트로 관리하는 방식입니다.

    • 오픈 어드레싱: 충돌이 발생하면 다른 빈 버킷을 찾아 데이터를 저장하는 방식입니다.

chevron-right연결 리스트로 연결된 데이터 조회 과정hashtag
  1. 해시 함수 적용: 조회하려는 키에 해시 함수를 적용하여 해당 키가 저장된 버킷을 결정합니다. 해시 함수는 주어진 키에 대해 일정한 해시 값을 계산하고, 그 해시 값에 해당하는 버킷을 반환합니다.

    예를 들어, 해시 함수가 다음과 같이 정의되어 있다고 가정합니다:

    key = 12에 대해 해시 함수는 다음과 같은 해시 값을 반환합니다.

    따라서 키 12버킷 2에 저장되어 있음을 알 수 있습니다.

  2. 해당 버킷의 연결 리스트 탐색: 해시 테이블에서 버킷 2로 이동하여 그 버킷에 저장된 연결 리스트를 조회합니다. 연결 리스트는 충돌로 인해 같은 해시 값을 가지는 여러 항목이 저장되어 있으므로, 이 리스트에서 원하는 키를 찾아야 합니다.

  3. 연결 리스트에서 키 비교: 연결 리스트의 각 노드를 순차적으로 탐색하며, 저장된 키와 조회하려는 키를 하나씩 비교합니다. 만약 같은 키를 찾으면 해당 키에 연결된 데이터를 반환합니다.

  4. 키가 일치하는 데이터 반환: 연결 리스트에서 조회하려는 키를 찾았다면, 해당 키에 저장된 데이터 값을 반환합니다.

  5. 키가 일치하지 않으면 조회 실패: 연결 리스트를 모두 탐색했지만 일치하는 키를 찾지 못하면, 해당 키에 대한 데이터는 해시 테이블에 존재하지 않음을 의미합니다.

클러스터링 인덱스에 대해서 설명해주세요

  • 데이터베이스 테이블의 물리적인 저장 순서를 인덱스의 순서와 일치시키는 방식(해당 인덱스의 값에 따라 데이터가 디스크에 물리적으로 정렬되어 저장)

  • 테이블당 단 하나만 존재 가능. 기본키 / 고유키에 따라 정렬됨

  • InnoDB와 같은 스토리지 엔진에서는 자동으로 기본키가 클러스터링 인덱스가 됨

  • 빠른 범위 검색

  • 인접 데이터 액세스

  • 데이터 수정 시 오버헤드(재배치)

인덱스 스캔 방식에 대해 설명해주세요.

인덱스 유니크 스캔

  • 인덱스 유니크 스캔고유한 값을 검색할 때 사용됩니다. 이 방식은 주로 프라이머리 키(Primary Key)유니크 인덱스(Unique Index)에 대해 정확한 값 일치 검색을 수행하는 경우 사용됩니다. 인덱스에서 해당 키를 바로 찾아가기 때문에 성능이 매우 빠릅니다.

인덱스 레인지 스캔

  • 검색할 데이터의 범위가 정해졌을 때 사용되는 스캔 방식 (WHERE, BETWEEN, <, > 등)

인덱스 시크

  • B-트리(B-Tree) 또는 B+트리(B+Tree) 구조를 사용하여 빠르게 특정 값 또는 좁은 범위의 값을 찾는 방식입니다.

  • 트리 구조를 활용하여 루트 노드부터 리프 노드까지 검색하면서 조건에 맞는 데이터를 효율적으로 찾습니다.

인덱스 풀 스캔

  • 인덱스의 처음부터 끝까지 모든 엔트리를 순차적으로 스캔하는 방식입니다.

  • 테이블 전체를 읽는 것이 아닌, 인덱스에 저장된 특정 열만 읽기 때문에 테이블 풀 스캔(Table Full Scan)보다 효율적일 수 있습니다. (왜냐하면 인덱스는 테이블의 전체 컬럼을 읽지 않고, 필요한 컬럼만 포함하고 있기 때문입니다.)

  • 모든 레코드를 인덱스를 통해 검색하지만, 트리 구조의 장점(빠른 탐색)을 활용하지 않고 순차적 탐색이 발생합니다.

    예시:

    만약 department_id에 인덱스가 있다면, 쿼리의 결과가 많은 데이터를 반환할 경우 인덱스 풀 스캔이 발생할 수 있습니다. 이때 테이블의 모든 열을 읽지 않고, 인덱스에 포함된 department_id와 관련된 데이터만을 순차적으로 스캔합니다.

인덱스 패스트 풀 스캔 (Index Fast Full Scan)

  • 인덱스를 정렬되지 않은 순서로 병렬로 읽는 방식입니다.

  • 일반적인 인덱스 풀 스캔보다 더 빠르게 인덱스를 조회할 수 있지만, 인덱스의 정렬이 유지되지 않으므로 범위 검색에 적합하지 않습니다.

  • 빠르게 데이터를 읽어와야 하지만 순서가 중요하지 않을 때 사용됩니다.

chevron-rightTable Full Scan vs Index Full Scanhashtag

테이블 풀 스캔은 모든 컬럼에 대해서 읽은 후에 조건을 확인합니다.

인덱스 풀 스캔은 일부 컬럼만 저장되어서 디스크에서 읽어야 하는 데이터 양이 적습니다.

레코드 수는 같아도, 읽어야 하는 총 데이터 양에서 차이가 납니다.

쿼리 실행 계획에 대해서 설명해주세요. 실행 계획을 확인해본 적이 있나요?

  • 데이터베이스가 SQL 쿼리를 어떻게 실행할지에 대한 상세한 계획

  • 옵티마이저의 실행 계획은 그 과정에서 테이블에 어떻게 접근할 것인지, 어떤 인덱스를 사용할 것인지, 조인을 어떻게 처리할 것인지 등을 보여줍니다.

  • EXPLAIN 키워드를 사용. EXPLAIN SELECT * FROM users WHERE age = 30;

쿼리힌트가 뭔가요?

  • 옵티마이저에게 특정 실행 계획을 따르도록 지시하는 것.

  • MySQL에서는 USE INDEX 같은 힌트를 사용할 수 있습니다. SELECT * FROM users USE INDEX (idx_age) WHERE age = 30;

인덱스가 잘 동작하고 있는지 어떻게 확인할 수 있을까요?

  • 쿼리 실행 계획(EXPLAIN)을 확인

  • 쿼리 성능 테스트

  • 시스템 테이블로 불필요한 인덱스 식별

인덱스 사용시 주의할점

  • 인덱스의 개수

  • 중복인덱스: 동일한 컬럼에 여러개의 인덱스

  • 카디널리티 고려 (유일한 값의 개수)

  • 충분히 데이터양이 큰지 확인

GROUP BY 사용 시 인덱스가 걸리는 조건에 대해 설명해주세요

  • 인덱스 컬럼의 순서: GROUP BY 절에서 사용하는 컬럼이 인덱스에 설정된 순서와 일치해야 인덱스를 사용 가능 (GROUP BY a,b,c - OK / GROUP BY b,c - X)

  • GROUP BY 구문에서 사용된 컬럼이 모두 인덱스에 포함되어야 합니다.

GROUP BY는 결과를 정렬하는데 인덱스는 이미 정렬되어 있기떄문에 그루핑이 가능해집니다.

chevron-right예시로 이해하기hashtag

1. 올바른 순서로 GROUP BY 하는 경우

인덱스가 (a, b, c) 순서로 설정된 경우를 가정해봅시다.

이 경우:

  • a를 기준으로 먼저 데이터를 정렬하고, b를 기준으로 그 안에서 다시 데이터를 정렬하며, 마지막으로 c를 기준으로 정렬된 데이터가 인덱스에 저장됩니다.

  • GROUP BY a, b, c는 인덱스의 정렬 순서를 그대로 따르므로, 인덱스를 그대로 사용하여 데이터를 효율적으로 그룹핑할 수 있습니다. 인덱스를 타고 내려가면서 정렬된 데이터를 빠르게 탐색할 수 있습니다.

2. 중간 컬럼을 건너뛰고 GROUP BY 하는 경우

이 경우:

  • 인덱스는 a를 기준으로 먼저 정렬되었기 때문에, a를 건너뛰고 b, c만으로 그룹핑하려는 경우 인덱스를 활용할 수 없습니다.

  • 이는 인덱스가 a로 먼저 정렬된 상태에서 b와 c로 그룹핑을 진행할 수 없기 때문입니다. 인덱스는 a의 값에 따라 첫 번째로 분류된 데이터 하위에서 b, c가 정렬된 상태이므로, a를 무시하고 b, c만을 기준으로 그룹핑하는 것이 불가능합니다.

3. 부분적으로 GROUP BY 하는 경우

이 경우:

  • 인덱스가 (a, b, c) 순서로 설정되어 있고, GROUP BY에서 ab만 사용하면 인덱스는 사용될 수 있습니다.

  • 인덱스는 a와 b까지는 정렬된 상태로 저장되어 있기 때문에, GROUP BY a, b는 인덱스를 활용하여 효율적으로 그룹핑할 수 있습니다. c는 무시되더라도 문제가 되지 않습니다.

GROUP BY b, c는 인덱스를 사용하지 못하는가?

  • 인덱스가 a부터 정렬된 상태이기 때문에, 인덱스는 a의 정렬 없이 b, c로 바로 접근할 수 없습니다.

  • B-트리 구조는 첫 번째 컬럼을 기준으로 데이터를 분류하고, 그 하위에서 두 번째, 세 번째 컬럼이 정렬되기 때문에, 첫 번째 컬럼을 건너뛰고 그다음 컬럼들만 사용한 GROUP BY는 인덱스가 트리의 정렬된 특성을 제대로 활용할 수 없게 만듭니다.

정리

  • 인덱스의 컬럼 순서GROUP BY 절의 컬럼 순서는 일치해야 합니다.

  • 첫 번째 컬럼을 기준으로 데이터가 정렬되기 때문에, GROUP BY에서 첫 번째 컬럼을 건너뛰면 인덱스가 제대로 사용되지 않습니다.

  • 부분적으로 GROUP BY에서 인덱스의 첫 번째부터 연속된 컬럼만 사용하면, 인덱스는 효과적으로 사용될 수 있습니다.

Last updated