열을 색인화 할 때 왜이 sqlite 쿼리가 훨씬 느려 집니까?


14

나는 (가짜) 사람들의 이름을 포함하는 두 개의 테이블이 각각 50,000 개의 행을 가진 sqlite 데이터베이스를 가지고 있습니다. 두 테이블에 공통적 인 이름 (이름, 중간 이니셜, 성)이 몇 개인 지 알아보기 위해 간단한 쿼리를 작성했습니다.

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;

기본 키를 제외하고 (이 쿼리와 관련이없는) 인덱스가 없으면 빠르게 실행됩니다.

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s

그러나 각 테이블의 세 열에 인덱스를 추가하면 (6 개의 인덱스) 모두 :

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.

그런 다음 고통스럽게 느리게 실행됩니다.

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys     0m50.696s

이것에 운율이나 이유가 있습니까?

EXPLAIN QUERY PLAN인덱스가없는 버전 의 결과는 다음과 같습니다 .

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)

이것은 색인과 함께입니다 :

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)

1
색인에 포함되지 않습니다. 각 열을 개별적으로 색인하는 것으로 보입니다. 인덱스 ( middleinitial, surnamegivenname) 에 세 열을 모두 포함하는 포함 인덱스를 만들면 어떻게됩니까 ?
랜돌프 웨스트

@ 랜 도프 웨스트 나는 당신이 의미하는 바를 이해하지만, 당신은 올바른 용어를 사용하고 있지 않습니다. 예를 들어, query SELECT c FROM t WHERE a=1 AND b=2의 경우 인덱스 t(a,b,c)는 포함하지만 포함 t(a,b)하지 않습니다. 인덱스를 포함하는 이점은 전체 쿼리 결과를 인덱스에서 직접 가져올 수있는 반면, 포함되지 않은 인덱스는 관련 행을 빠르게 찾을 수 있지만 여전히 주 테이블 데이터를 참조하여 값을 선택해야한다는 것입니다.
Arthur Tacca

답변:


15

SQLite에서 조인은 중첩 루프 조인으로 실행됩니다. 즉, 데이터베이스가 한 테이블을 통과하고 각 행마다 다른 테이블에서 일치하는 행을 검색합니다.

인덱스가 있으면 데이터베이스는 인덱스에서 일치하는 항목을 빠르게 찾은 다음 해당 테이블 행으로 이동하여 필요한 다른 열의 값을 얻을 수 있습니다.

이 경우 가능한 세 가지 색인이 있습니다. 통계 정보 ( ANALYZE 를 실행하여 생성됨)가 없으면 데이터베이스는 가장 작은 정보를 선택하여 I / O를 줄입니다. 그러나 middleinitial인덱스는 페치해야하는 테이블 행 수를 크게 줄이지 않기 때문에 쓸모가 없습니다. 인덱스를 통한 추가 단계는 실제로 테이블 행을 더 이상 순서대로 읽지 않고 무작위로 가져 오기 때문에 필요한 I / O를 실제로 증가시킵니다.

인덱스가 없으면 일치하는 행을 찾아 보려면 첫 번째 테이블의 각 행에 대해 두 번째 테이블의 전체 테이블 스캔이 필요합니다. 데이터베이스가이 쿼리에 대해서만 임시 색인을 작성하고 삭제하는 것이 가치가 있다고 추정하기에는 너무 나쁩니다. 이 임시 ( "AUTOMATIC") 색인은 검색에 사용 된 모든 열람에서 작성됩니다. COUNT (*) 연산에는 다른 열의 값이 필요하지 않으므로이 인덱스커버링 인덱스 이므로 인덱스 항목에 해당하는 테이블 행을 실제로 조회 할 필요가 없으므로 훨씬 더 많은 비용을 절약 할 수 있습니다. /영형.

이 쿼리 속도를 높이려면이 인덱스를 영구적으로 작성하여 더 이상 임시 인덱스를 구성 할 필요가 없습니다.

CREATE INDEX uk_all_names ON fakenames_uk(surname, givenname, middleinitial);

EXPLAIN QUERY PLAN
SELECT count(*)
FROM fakenames_uk
JOIN fakenames_usa USING (givenname, middleinitial, surname);

0|0|1|SCAN TABLE fakenames_usa
0|1|0|SEARCH TABLE fakenames_uk USING COVERING INDEX uk_all_names (surname=? AND givenname=? AND middleinitial=?)

의 인덱스는 surname세 열 인덱스가이 칼럼에 어떤 조회에 사용 할 수 있기 때문에 더 이상 필요하지 않습니다. 이 열에서만 조회를 수행 할 경우
인덱스 on givenname이 유용 할 수 있습니다.
인덱스 middleinitial는 항상 쓸모가 없습니다. 26 개의 가능한 값 중 하나를 검색하는 쿼리는 전체 테이블을 스캔하면 빠릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.