2012-03-16 1 views
12

イメージをアップロードしてタグ付けできるImageアップロードサービスのかなり基本的な実装があります。これは私のスキーマです:一時的な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フィールドがあるがあることが、これはクエリの速度に大きな影響を与えません。

これはインターネットなので、このサービスの画像の大部分は猫であり、「cat」というタグが付けられています。実際には、50,000枚の画像のうち約47,000枚が「猫」とタグ付けされています。 「猫」でタグ付けされたすべての画像を取得するためのクエリは、ここでの主な問題は、注文の最後の行、のUSE 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; 

ですBY。これにより、クエリが大幅に遅くなります。 'order by'句がないと、クエリ全体が約0.001秒で実行されます。 order by句では、クエリには0.483秒かかります。これは400倍のパフォーマンス上のペナルティです。

このクエリは0.1秒未満で取得したいと思いますが、わかりません。私は他の多くのクエリを試してインデックスを追加したり削除したりしていますが、これは私が実行できる最速のものです。

+1

... 'cat'でタグ付けする衝動に抵抗する。真剣に、しかし、問題の素晴らしい仕事、非常に詳細。 – bernie

+0

私はどこにも行かなかったので、私の答えを削除しました。あなたがそれを解決する方法を見つけたら、ここに投稿して@mentionして見せてくれれば嬉しいです。 – Tomalak

+0

@Tomalak:ここに答えがあります。 – Quassnoi

答えて

3

これは、フィルタリングと順序インデックスの間で選択の一般的な問題である:あなたは人気のあるタグのリストを維持する必要があり

は、(そのための順序インデックスは、より有益です)タグが一般的であればフィルタリングインデックスを禁止します(例:

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 
     i.uploadedDate DESC 
LIMIT 20 

また、スキーマを非正規化して、uploadedDateTagBridgeに追加し、トリガーなどで埋めてもかまいません。その後TagBridge (pid_high, pid_low, uploadedDate, image_id_high, image_id_low)に複合インデックスを作成し、クエリを少し書き換える:

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; 

SQLiteがタプル構文を理解していないので、二重のサブクエリがあります。

+0

+1逆正規化はかなりうまくいくでしょう。もちろん、すべての 'uploadedDate'値を同期させるためにタグが変更された場合は注意が必要です。私はまた、他のアイデアが好きです。 – Tomalak

関連する問題