오라클은 왜 "완벽한"지수를 무시합니까?
-
20-09-2019 - |
문제
이 테이블이 있습니다.
create table demo (
key number(10) not null,
type varchar2(3) not null,
state varchar2(16) not null,
... lots more columns ...
)
그리고이 지수 :
create index demo_x04 on demo(key, type, state);
이 쿼리를 실행할 때
select * from demo where key = 1 and type = '003' and state = 'NEW'
EXPLAIN PLAN
전체 테이블 스캔을 수행 함을 보여줍니다. 그래서 나는 색인을 삭제하고 다시 만들었습니다. EXPLAIN PLAN
여전히 전체 테이블 스캔이 표시됩니다. 어떻게 그렇게 될수 있니?
일부 배경 : 이것은 역사적 데이터이므로, 상태와 함께 행을 찾는 것입니다. CLEARED
상태와 함께 새 행을 삽입하십시오 NEW
(또한 이전 행에서 몇 가지 값을 복사합니다). 그런 다음 이전 행이 업데이트됩니다 USED
. 그래서 테이블은 항상 자랍니다. 내가 알아 차린 것은 인덱스의 카디널리티가 0이라는 것입니다 (수천 가지 값이 있다는 사실에도 불구하고). 재현 후 카디널리티가 커졌지 만 CBO는 지수를 더 좋아하지 않았습니다.
다음날 아침, 오라클은 갑자기 색인을 좋아했고 (아마도 그 위에 잤을 것입니다) 사용하기 시작했지만 오래 사용하지 않았습니다. 잠시 후, 처리는 50 행/s에서 3 행으로 떨어졌고 "전체 테이블 스캔"을 다시 보았습니다. 무슨 일이 일어나고 있습니까?
제 경우에는 약 백만 행을 처리해야합니다. 나는 ca. 50. 인덱스를 업데이트/재개하겠다고 커밋 한 후에 실행 해야하는 명령이 있습니까?
나는 Oracle 10G에 있습니다.
편집]이 표에는 969'491 개별 키, 3 개의 유형 및 3 개의 상태가 있습니다.
해결책
인덱스 힌트를 지정하면 어떻게됩니까? 이 시도:
SELECT /*+ INDEX (demo demo_x04) */ *
FROM demo
WHERE key = 1
AND type = '003'
AND state = 'NEW';
밤새 일어난 일은 테이블이 분석되었다는 것 같습니다. 그런 다음 테이블에 대한 처리를 실행하면서 Oracle의 테이블 통계가 다시 부실하게 만들기 위해 충분한 인덱스가 업데이트되었고 Optimizer는 인덱스 사용을 중단했습니다.
힌트를 추가하고 설명 계획이 다른 계획을 제공하고 쿼리가 더 잘 수행되는지 확인하십시오.
오, 그리고 테이블 분석에 관한 Tony의 답변은 일반적인 모범 사례이지만, 10G는 데이터베이스가 그와 관련하여 자기 유지 보수를하는 데 매우 좋습니다. 프로세스가 많은 업데이트를 수행하면 색인이 빨리 오래 될 수 있습니다. 프로세스가 시작될 때 분석을 실행하면 상황이 한동안 개선되면 이것이 문제라는 것을 알게 될 것입니다.
테이블에 대한 통계를 업데이트하려면 dmbs_stats.gather_table_stats 패키지.
예를 들어:
exec dbms_stats.gather_table_stats ( '소유자', '데모');
다른 팁
최근에 테이블이 분석 되었습니까? Oracle이 그것이 매우 작다고 생각하면 인덱스 사용을 고려하지 않을 수도 있습니다.
이 시도:
select last_analyzed, num_rows
from user_tables
where table_name = 'DEMO';
Num_rows는 Oracle이 테이블에 포함 된 몇 개의 행 수를 알려줍니다.
"다음날 아침, 오라클은 갑자기 지수를 좋아했습니다 (아마도 그 위에 잤을 것입니다)"아마도 DBMS_STATS가 밤새도록 실행 중일 것입니다.
일반적으로 나는 색인을 전체 테이블 스캔 한 세 가지 이유 중 하나를 볼 수 있습니다. 첫 번째는 Optimizer가 테이블이 비어 있거나 적어도 매우 작다고 생각한다는 것입니다. 나는 이것이 초기 문제라고 생각합니다. 이 경우 인덱스를 사용하기보다는 소수의 블록으로 구성된 테이블을 전체 스캔하는 것이 더 빠릅니다.
두 번째는 쿼리가 인덱스를 실제로 사용할 수없는 경우입니다.
"select * from demo where key = 1 and type = '003' and state = 'NEW'"
실제로 쿼리에서 하드 코딩 된 리터럴을 사용하고 있습니까? 그렇지 않은 경우 변수 데이터 유형이 잘못 될 수 있습니다 (예 : 키는 문자). 이는 숫자 키를 비교를 위해 문자로 변환해야하므로 색인이 거의 쓸모가 없게됩니다.
세 번째 이유는 쿼리가 테이블의 많은 행을 처리 할 것이라고 생각하는 곳입니다. 유형과 상태는 카디널리티가 매우 낮아 보입니다. 특정 '키'값이 많습니까?
당신이 설명하는 처리에 대한 의견 : 간헐적 커밋으로 행으로 처리하는 것처럼 들리며, 가능하다면 이것을 다시 생각할 것을 촉구합니다. 업데이트/삽입 메커니즘은 합병 명령문으로 잘 변환 될 수 있으며 전체 데이터 세트는 끝에 하나의 커밋으로 단일 문으로 처리 될 수 있습니다. 이것은 현재 방법보다 거의 더 빠르고 리소스를 적게 사용합니다.
열 키의 값은 항상 1입니까? 그렇다면 각 행을 검사해야하므로 인덱스 컨설팅이 쿼리를 최적화 할 것이라고 확신하지 못합니다. 그렇다면 키 열이없는 색인을 선언하십시오. 당신은 또한 시도 할 수 있습니다 :
select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'
(내 추측이 옳은 경우) 각 행을 계속 살펴 봐야하지만 결과 세트의 모든 열이 이제 덮여 있기 때문에 인덱스로 이동할 수 있습니다.
나는 당신의 진술에 기초하여 인덱스가 카디널리티 0을 보여준다는 것을 추측하고 있습니다.