느리게 실행되는 쿼리의 실행 계획을 살펴본 결과 일부 노드는 인덱스 검색이고 일부는 인덱스 스캔 인 것으로 나타났습니다.
인덱스 검색과 인덱스 스캔의 차이점은 무엇입니까?
어느 것이 더 잘 수행됩니까?
SQL은 어떻게 다른 것을 선택합니까?
나는 이것이 3 가지 질문이라는 것을 알고 있지만 첫 번째 질문에 대답하면 다른 질문에 대해 설명 할 것이라고 생각합니다.
느리게 실행되는 쿼리의 실행 계획을 살펴본 결과 일부 노드는 인덱스 검색이고 일부는 인덱스 스캔 인 것으로 나타났습니다.
인덱스 검색과 인덱스 스캔의 차이점은 무엇입니까?
어느 것이 더 잘 수행됩니까?
SQL은 어떻게 다른 것을 선택합니까?
나는 이것이 3 가지 질문이라는 것을 알고 있지만 첫 번째 질문에 대답하면 다른 질문에 대해 설명 할 것이라고 생각합니다.
답변:
짧은 버전 : 탐색이 훨씬 좋습니다
덜 짧은 버전 : 찾기는 일반적으로 훨씬 낫지 만 많은 검색 (예를 들어 불쾌한 상관 하위 쿼리가있는 잘못된 쿼리 디자인으로 인해 또는 커서 작업이나 다른 루프에서 많은 쿼리를 생성하기 때문에)은 특히 쿼리가 영향을받는 테이블의 대부분의 행에서 데이터를 반환 할 수있는 경우 스캔하십시오.
데이터 검색 작업이 전체 가족을 포괄하여 성능 관련 사항을 완전히 이해하는 데 도움이됩니다.
테이블 스캔 : 쿼리와 관련된 인덱스가 전혀 없으면 플래너는 모든 행을 볼 수있는 테이블 스캔을 사용해야합니다. 이로 인해 테이블의 데이터와 관련된 모든 페이지가 디스크에서 읽혀질 수 있으며 이는 최악의 경우입니다. 일부 쿼리의 경우 유용한 인덱스가있는 경우에도 테이블 스캔을 사용합니다. 이는 일반적으로 테이블의 데이터가 너무 작아 인덱스를 순회하기가 더 어렵 기 때문입니다 (이 경우 예상되는 경우 지수의 선택성 측정이 양호하다고 가정 할 때 데이터가 증가함에 따라 변경 계획).
행 조회가있는 인덱스 스캔 : 검색에 직접 사용할 수있는 인덱스가 없지만 올바른 열을 포함하는 인덱스가 있으면 인덱스 스캔이 사용될 수 있습니다. 예를 들어 column1, col2, col3에 인덱스가있는 20 개의 열이있는 큰 테이블이 있고을 발행 SELECT col4 FROM exampletable WHERE col2=616
하는 경우이 경우 쿼리 할 인덱스를 col2
스캔하는 것이 전체 테이블을 스캔하는 것보다 낫습니다. 일치하는 행이 발견되면 쿼리 페이지에서 볼 때 "책갈피 조회"단계 인 출력 (또는 추가 결합)을 위해 col4를 픽업하기 위해 데이터 페이지를 읽어야합니다.
행 조회가없는 인덱스 스캔 : 위의 예에서 SELECT col1, col2, col3 FROM exampletable WHERE col2=616
데이터 페이지를 읽는 데 추가 노력이 필요하지 않습니다. 인덱스 행 일치 col2=616
가 발견되면 요청 된 모든 데이터가 알려져 있습니다. 그렇기 때문에 때때로 검색되지는 않지만 색인 끝에 추가되어 출력을 요청할 가능성이있는 열이 표시되는 경우가 있습니다. 행 조회를 저장할 수 있습니다. 이러한 이유로 인덱스에 열을 추가 할 때이 열만 추가 INCLUDE
하면 엔진에 이러한 열을 기반으로하는 쿼리에 대한 인덱스 레이아웃을 최적화 할 필요가 없음을 알립니다 (이러한 열에 대한 업데이트 속도를 높일 수 있음) . 인덱스 스캔도 필터링 절이없는 쿼리에서 발생할 수 있습니다 SELECT col2 FROM exampletable
. 테이블 페이지 대신이 예제 인덱스를 스캔합니다.
인덱스 검색 (행 검색이 있거나없는) : 검색시 모든 인덱스가 고려되지는 않습니다. 쿼리의 SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
경우 쿼리 엔진은 인덱스에서 트리 기반 검색을 수행하여 일치하는 첫 번째 행을 c1
찾은 다음 범위의 끝에 도달 할 때까지 인덱스를 순서대로 탐색 할 수 있습니다 (쿼리와 동일 함) 왜냐하면 조작 c1=1234
조차도 조건과 일치하는 많은 행이있을 수 있기 때문입니다 =
). 즉, 인덱스 (또는 테이블)의 모든 페이지 대신 관련 인덱스 페이지 (초기 검색에 몇 개만 필요) 만 읽어야합니다.
클러스터형 인덱스 : 클러스터형 인덱스를 사용하면 테이블 데이터가 별도의 힙 구조가 아닌 해당 인덱스의 리프 노드에 저장됩니다. 즉, TEXT
열이나 VARCHAR(MAX)
긴 데이터를 포함하는 열과 같은 페이지 외부 데이터가없는 한 어떤 인덱스가 필요한지에 관계없이 해당 인덱스를 사용하여 행을 찾은 후에는 추가 행 조회가 필요하지 않습니다 .
이런 이유로 클러스터 된 인덱스는 하나만 가질 수 있습니다 [1] , 클러스터 된 인덱스 는 별도의 힙 구조를 갖지 않고 테이블이므로, [2] 를 사용하는 경우 최대 이득을 얻기 위해 신중하게 배치 할 위치를 선택하십시오.
또한 테이블의 "클러스터링 키"가 테이블의 모든 클러스터되지 않은 인덱스에 포함되므로 클러스터 된 인덱스이므로 넓은 클러스터 된 인덱스는 일반적으로 좋지 않습니다.
[1] 실제로, 테이블의 모든 열을 포함하거나 포함하는 비 클러스터형 인덱스를 정의하여 여러 개의 클러스터형 인덱스를 효과적으로 가질 수 있지만, 이는 공간을 낭비 할 수 있으므로 쓰기 성능에 영향을 줄 수 있습니다. 당신은 정말로 필요합니다.
내가 말할 때 "클러스터 된 인덱스를 사용하는 경우"[2], 일반적으로 당신이하는 것이 좋습니다주의 할 할 각 테이블에 하나가 있습니다. 모든 일반적인 규칙과 마찬가지로 예외적으로 대량 삽입 및 정렬되지 않은 읽기 (ETL 프로세스의 스테이징 테이블) 이외의 테이블은 가장 일반적인 카운터 예입니다.
추가 사항 : 불완전한 스캔 :
나머지 쿼리에 따라 테이블 / 인덱스 스캔이 실제로 전체 테이블을 스캔하지 않을 수도 있다는 점을 명심해야합니다. 로직으로 인해 쿼리 계획에 따라 쿼리 계획이 중단 될 수 있습니다. 가장 간단한 예는 SELECT TOP(1) * FROM HugeTable
-쿼리 계획을 살펴보면 스캔에서 하나의 행만 반환되고 IO 통계 ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
)를 보면 아주 작은 숫자 만 읽는다는 것을 알 수 있습니다 페이지 수 (아마도 하나).
WHERE
or JOIN ... ON
절의 술어가 데이터 인 경우 소스 인 스캔과 동시에 실행될 수있는 경우에도 마찬가지 입니다. 쿼리 플래너 / 주자는 가끔 이런 식으로 스캔의 조기 종료를 할 수 있도록 데이터 소스쪽으로 다시 술어를 밀어 매우 영리한 될 수있다 (때로는 당신이 그렇게 할 수 있도록 쿼리를 다시 배열의 영리한 될 수 있습니다!). 표준 조회 계획 표시의 화살표에 따라 데이터 가 오른쪽에서 왼쪽으로 흐르지 만 로직 은 왼쪽에서 오른쪽으로 실행되며 다음 단계를 시작하기 전에 각 단계 (오른쪽에서 왼쪽으로)가 완료 될 때까지 실행되지는 않습니다. 위의 간단한 예에서 상담원으로 쿼리 계획의 각 블록을 보면 상담원은 SELECT
상담원에게 TOP
행을 요청합니다.TABLE SCAN
상담원은 SELECT
다른 상담원을 요청하지만 TOP
상담원은 테이블 리더를 요구할 필요가 없다는 것을 알고 SELECT
상담원은 "더 이상 관련이 없습니다"라는 응답을 받고 모든 작업이 완료된 것을 알고 있습니다. 많은 작업 테이블 / 인덱스 스캔이 정말로 더 복잡한 예제에서 자주 물론 최적화 이런 종류의 차단 않는 모든 행을 읽을 수 있지만 어떤 검사가 비용이 많이 드는 작업해야 결론으로 이동하지 않도록주의.
일반적으로 검색은 좋고 스캔은 좋지 않습니다.
검색은 쿼리에서 인덱스를 효과적으로 사용하고 필요한 행을 찾는 데 사용할 수있는 곳입니다.
스캔은 쿼리가 전체 인덱스를 통해 필요한 것을 찾으려고하는 곳입니다.
SQL은 어떻게 선택합니까? 쿼리 최적화 프로그램 내부에서 쿼리 및 사용 가능한 인덱스 및 해당 인덱스와 관련된 통계 정보를 기반으로 결정됩니다.
여기서 읽을만한 몇 가지 책이 있습니다. http://www.red-gate.com/community/books/ 의 Red-Gate 서점에서
이 주제를 파고 싶다면 매우 유용한 책 (적어도 저에게는)은 Grant Fritchey의 SQL Server 실행 계획이며 RedGate 에서 무료로 제공 됩니다 .
다음과 같은 검색어가있는 경우
SELECT *
FROM myTable
SQL Server는 필요한 결과를 표시하기 위해 모든 행을 통과해야하므로 인덱스 스캔을 사용합니다.
반대로
SELECT *
FROM myTable
WHERE myID = 1
확실히 인덱스 탐색을 초래할 것입니다. SQL Server는 myID 인덱스 의 B- 트리 구조 를 사용하며 적절한 행을 검색하는 것이 훨씬 빠릅니다.
다른 사람들은 탐색과 스캔의 차이점을 충분히 정의했습니다. 이 경우 쿼리 자체와 실행 플래너는 각 파트에서 쿼리의 조건 자 (필터)로 사용되는 값을 확인하는 데 필요한 정보를 제공해야합니다. 일반적으로 항상 외래 키에 클러스터되지 않은 인덱스를 추가하는 것이 좋습니다. 프로그램 코드의 사용 사례에 따라 추가 다중 열 인덱스 또는 포함 된 열 인덱스를 만드는 것이 좋습니다. 여기에 제시된 용어를 사용하면 Google 검색에서 각 예제에 대한 적절한 결과를 얻을 수 있습니다.
그러나 예를 들어 코드가 주어진 필터에서 열 A와 열 B를 쿼리한다고 가정하지만 열 C와 열 E의 값을 반환하려면 INCLUDE를 사용하여 열 A와 B에 인덱스를 만들 수 있습니다 열 C와 E를 포함하는 옵션입니다. 단일 인덱스 검색은 동일한 행에서 다른 값 (C 및 E)을 검색하기 위해 조회 할 필요가 없으므로 필요한 모든 것을 반환합니다.