2012-03-16 4 views
12

이미지 업로드 및 태그 지정을 수행 할 수있는 이미지 업로드 서비스를 구현했습니다. 이건 내 스키마입니다 :임시 B 트리 제거 SQLite 쿼리에서 정렬

CREATE TABLE Tag(
    orm_id INTEGER PRIMARY KEY AUTOINCREMENT, 
    pid_high UNSIGNED BIG INT NOT NULL, 
    pid_low UNSIGNED BIG INT NOT NULL, 
    name STRING NOT NULL, 
    CONSTRAINT KeyConstraint UNIQUE (pid_high, pid_low) ON CONFLICT FAIL); 

CREATE TABLE TagBridge(
    orm_id INTEGER PRIMARY KEY AUTOINCREMENT, 
    pid_high UNSIGNED BIG INT NOT NULL, 
    pid_low UNSIGNED BIG INT NOT NULL, 
    image_id_high UNSIGNED BIG INT NOT NULL, 
    image_id_low UNSIGNED BIG INT NOT NULL, 
    tag_id_high UNSIGNED BIG INT NOT NULL, 
    tag_id_low UNSIGNED BIG INT NOT NULL, 
    CONSTRAINT KeyConstraint UNIQUE (pid_high, pid_low) ON CONFLICT FAIL); 

CREATE TABLE Image(
    orm_id INTEGER PRIMARY KEY AUTOINCREMENT, 
    pid_high UNSIGNED BIG INT NOT NULL, 
    pid_low UNSIGNED BIG INT NOT NULL, 
    filehash STRING NOT NULL, 
    mime STRING NOT NULL, 
    uploadedDate INTEGER NOT NULL, 
    ratingsAverage REAL, 
    CONSTRAINT KeyConstraint UNIQUE (pid_high, pid_low) ON CONFLICT FAIL); 

그리고 지수

CREATE INDEX ImageTest on Image(pid_high, pid_low, uploadedDate DESC); 
CREATE INDEX ImagefilehashIndex ON Image (filehash); 
CREATE INDEX ImageuploadedDateIndex ON Image (uploadedDate); 
CREATE INDEX TagnameIndex ON Tag (name); 

이유는,이 서비스는 클라이언트 권한을 128 비트 GUID를 사용하기 때문에 대신 표준 기본 키의 pid_high/pid_low 필드가 있다는 것을하지만, 이 쿼리 속도를 크게 영향을주지 않습니다.

인터넷이므로이 서비스에있는 대부분의 이미지는 고양이이며 '고양이'라는 태그가 지정되어 있습니다. 사실 50,000 개 이미지 중 약 47,000 개가 '고양이'라는 태그가 지정되어 있습니다. '고양이'태그 모든 이미지를 얻을 수있는 쿼리는 여기에 가장 큰 문제는 주문에 대한 마지막 행, 사용의 TEMP의 B-TREE입니다 이것에 대한 쿼리 계획이

sele order   from deta 
---- ------------- ---- ---- 
0  0    0  SEARCH TABLE Tag AS t USING INDEX TagnameIndex (name=?) (~1 rows) 
0  1    1  SCAN TABLE TagBridge AS b (~472 rows) 
0  2    2  SEARCH TABLE Image AS i USING INDEX ImageTest (pid_high=? AND pid_low=?) (~1 rows) 
0  0    0  USE TEMP B-TREE FOR ORDER BY 

입니다

select i.* from Tag t, TagBridge b, Image i 
where 
    b.tag_id_high = t.pid_high AND b.tag_id_low = t.pid_low 
AND b.image_id_high = i.pid_high and b.image_id_low = i.pid_low 
AND t.name ='cat' 
order by uploadedDate DESC LIMIT 20; 

입니다 으로. 이렇게하면 쿼리 속도가 크게 느려집니다. 'order by'절이 없으면 전체 쿼리를 실행하는 데 약 0.001 초가 걸립니다. order by 절을 사용하면 쿼리에 0.483 초가 걸리고 400 배의 성능 저하가 발생합니다.

이 쿼리를 0.1 초 미만으로 얻고 싶습니다. 그러나 확실하지 않습니다. 나는 다른 많은 쿼리를 시도하고 인덱스를 추가 및 제거했지만 이것이 내가 실행할 수있는 가장 빠른 것입니다.

+1

... '고양이'라는 질문에 대한 충동에 저항하지 않습니다. 진지하게, 질문에 좋은 일, 아주 자세하게. – bernie

+0

아무데도 가지 않았기 때문에 내 대답을 삭제했습니다. 만약 당신이 그것을 해결할 방법을 찾으면 여기에 게시하고 @mention 나를 볼 수 있도록 기뻐할 것입니다. – Tomalak

+0

@Tomalak : 여기에 답이 있습니다. – Quassnoi

답변

3

이 필터링 및 주문 지수 사이의 선택의 일반적인 문제 : 당신은 인기있는 태그 목록을 유지해야

는 (있는 주문 지수가 더 도움이됩니다) 예를 들어 태그가 인기있는 경우 필터 색인을 금지하는 이유는 다음과 같습니다.

또는 스키마를 비정규 화하여 uploadedDateTagBridge에 추가하여 트리거 또는 기타로 채우십시오. 그런 다음 TagBridge (pid_high, pid_low, uploadedDate, image_id_high, image_id_low)에 복합 인덱스를 생성하고 쿼리 조금을 다시 작성 : SQLite는 튜플 구문을 이해하지 않기 때문에

SELECT i.* 
FROM TagBridge b, Image i 
WHERE b.tag_id_high = 
     (
     SELECT t.pid_high 
     FROM Tag t 
     WHERE t.name = 'cat' 
     ) 
     AND b.tag_id_low = 
     (
     SELECT t.pid_low 
     FROM Tag t 
     WHERE t.name = 'cat' 
     ) 
     AND i.pid_high = b.image_id_high 
     AND i.pid_low = b.image_id_low 
ORDER BY 
     b.uploadedDate DESC 
LIMIT 20; 

이중 하위 쿼리입니다.

+0

+1 역 정규화는 꽤 잘 작동합니다. 물론 모든 uploadedDate 값을 동기화하기 위해 태그가 변경되면주의를 기울여야합니다. 나도 다른 생각을 좋아해. – Tomalak