배경더 빠른 FTS4 쿼리 결과를 다른 테이블의 필드로 정렬하려면 어떻게합니까?
나는를 활용, SQLite는에 저장된 전자 메일 메시지의 몸 전체 텍스트 검색을 구현하고있어 환상적인 내장 FTS4 엔진. 정확히 예상하지 못한 부분이 있지만 쿼리 성능이 좋지 않습니다. 한 번 보자.
적용 나는 전체 코드에 대한 링크, 문제의 코드의 몇 가지 간단한 예제를주지
대표 스키마.
CREATE TABLE MessageTable (
id INTEGER PRIMARY KEY,
internaldate_time_t INTEGER
);
CREATE INDEX MessageTableInternalDateTimeTIndex
ON MessageTable(internaldate_time_t);
검색 가능한 텍스트가 FTS4 테이블에 추가됩니다 : 우리는 (풀 버젼 here을 여러 개의 파일 here, here 퍼져 등) 전자 메일 메시지에 대한 데이터를 저장하는 MessageTable
있어
이름 MessageSearchTable
(전체 버전 here) : 메시지에 대한 외부 키로 검색 테이블 행위에
CREATE VIRTUAL TABLE MessageSearchTable USING fts4(
id INTEGER PRIMARY KEY,
body
);
id
표.
독자가이 테이블에 데이터를 삽입하는 연습으로 남겨 두겠습니다. (물론 개인 이메일은 제공 할 수 없습니다). 각 테이블에 26k 미만의 기록이 있습니다. 우리는 검색 결과를 검색 할 때
, 우리는 그들을 필요로
문제 쿼리는 그래서 우리는 가장 최근의 몇 가지 결과를 뽑아 수 internaldate_time_t
으로 내림차순으로 정렬합니다. 내 컴퓨터에
SELECT id
FROM MessageSearchTable
JOIN MessageTable USING (id)
WHERE MessageSearchTable MATCH 'a'
ORDER BY internaldate_time_t DESC
LIMIT 10 OFFSET 0
, 내 이메일로를 통해 측정, 약 150 밀리 초 단위로 실행 : 예를 들면 다음과 같습니다 검색 쿼리 (전체 버전 here)의
time sqlite3 test.db <<<"..." > /dev/null
150 밀리 것은 전혀 짐승입니다 쿼리가 있지만 단순한 FTS 조회 및 인덱싱 된 주문에 대해서는 부진합니다. ORDER BY
을 생략하면 10 밀리 초 단위로 완료됩니다. 또한 실제 쿼리에는 하나 이상의 하위 선택이 있으므로 일반적으로 조금 더 많은 작업이 진행됩니다. 쿼리의 전체 버전은 약 600 밀리 초 안에 실행되며 이는 짐승의 영역에 속하므로 ORDER BY
은 생략합니다. 사건은 500 밀리 세컨드를 면도했다. 내가 sqlite3
내부 통계를 켜고 쿼리를 실행하면
, 내가 선을주의 사항 :
docs about those stats의 내 해석이 맞다면 쿼리가 완전히MessageTableInternalDateTimeTIndex
를 사용하여 건너 뛰는 것처럼 Sort Operations: 1
, 그것은 보인다. 이 곳 테이블을 걷고 있지만,의 지금은 그것을 무시 할 수처럼
Fullscan Steps: 25824
는 소리 : 쿼리의 전체 버전은 라인을 가지고있다.
내가
을 발견했습니다 무엇 그래서 조금 것을 최적화 작업을 할 수 있습니다. 나는 INDEXED BY
확장자를 가진 우리의 인덱스를 사용하는 서브 선택 힘 SQLite는에 쿼리를 다시 정렬 할 수 있습니다 :
SELECT id
FROM MessageTable
INDEXED BY MessageTableInternalDateTimeTIndex
WHERE id IN (
SELECT id
FROM MessageSearchTable
WHERE MessageSearchTable MATCH 'a'
)
ORDER BY internaldate_time_t DESC
LIMIT 10 OFFSET 0
보라 보라, 실행 시간의 정식 버전 (약 100 밀리 초 300 밀리 초를 떨어졌다 쿼리, 실행 시간이 50 % 감소) 정렬 작업이보고되지 않습니다. 위와 같이 쿼리를 재구성하고 인덱스를 으로 강제 실행하지 않으면 여전히 정렬 작업이 수행됩니다 (아직 몇 밀리 초 밖에 안되었습니다). 따라서 SQLite는 강제로 처리하지 않으면 실제로 인덱스를 무시합니다. 그것.
나는 또한이 차이를 만들 것입니다 있는지 확인하기 위해 몇 가지 다른 것들을 시도했습니다,하지만 그들은하지 않았다 :
- 가 명시 적으로 그리고없이 설명 here로 인덱스
DESC
을INDEXED BY
- 나는이 순간에 기억할 수로 및
INDEXED BY
- 아마 몇 가지 다른 일없이 명시 적으로하고
internaldate_time_t
않고, 인덱스에id
열을 추가는DESC
을 주문 개
질문 여기
100 밀리 초는 여전히 간단한 FTS 조회 및 인덱스 순서로해야 것 같아 무엇에 대한 지독하게 느린 것 같다.
- 여기 어떻게됩니까? 왜 손을 강제로 사용하지 않으면 명백한 색인을 무시합니까?
- 가상 테이블과 일반 테이블의 데이터를 결합 할 때 몇 가지 제한이 있습니까?
- 왜 아직도 상대적으로 느리고 다른 테이블의 필드에 정렬 된 FTS 일치를 얻으려면 다른 방법이 있습니까?
감사합니다.
"쿼리에서 액세스 된 각 테이블에 둘 이상의 인덱스를 사용할 수 없습니다."- 그건 사실상 아닙니다. 사실입니다. [이 섹션] (http://www.sqlite.org/eqp.html#section_1_1)의 끝 부분을 참조하십시오. 그러나 내부적으로 두 개의 별도 쿼리를 실행하고 결과를 'UNION'하는 것으로 양보합니다. – chazomaticus
"MessageSearchTable의 행 조회 중 세 가지 작업을 수행하고 있습니다 ..."- 목표는 기본 키만 검색하는 것이 아니라 _rows_를 검색하는 것입니다. 모든 인덱스는 커버 인덱스로 간주되어야합니다 (쿼리 [planning] (http://www.sqlite.org/queryplanner.html) 및 [최적화] (http : //www.sqlite. org/optoverview.html) docs), 따라서 _all_ 테이블 스캔은 피할 수 있어야합니다. – chazomaticus
"메시지 시간을 FTS 테이블의 일부로 만듭니다."- FTS 테이블에 대한 나의 이해는 임의의 데이터를 추가 할 수 없으며 FTS 알고리즘에 의해 인덱싱 된 텍스트 일뿐입니다. 틀렸어? FTS 테이블에 열을 추가하고 주문할 수 있습니까? – chazomaticus