2012-03-16 12 views
12

Tengo una implementación bastante básica de un servicio de carga de imágenes, donde puede cargar imágenes y etiquetarlas. Este es mi esquema:Eliminar un árbol temporal B Ordenar de una consulta 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); 

e índices

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); 

La razón por la que hay campos pid_high/pid_low en lugar de la clave principal norma es porque este servicio utiliza los GUID de 128 bits cliente-autorizada, pero esto no afecta significativamente la velocidad de consulta.

Dado que se trata de Internet, la gran mayoría de las imágenes de este servicio son gatos y están etiquetadas con 'gato'. De hecho, aproximadamente 47,000 de 50,000 imágenes están etiquetadas con 'gato'. La consulta para obtener todas las imágenes con la etiqueta de 'gato' es

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; 

el plan de consulta para esto es

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 

El problema principal aquí es la última fila, USO TEMP B-ÁRBOL DE ORDEN POR. Esto ralentiza la consulta significativamente. Sin la cláusula 'ordenar por', toda la consulta tarda alrededor de 0.001 segundos en ejecutarse. Con la cláusula order by, la consulta requiere 0.483s, que es una penalización de rendimiento de 400x.

Me gustaría obtener esta consulta en menos de 0.1 segundos, pero no estoy seguro de cómo. He intentado muchas otras consultas y agregado y eliminado índices, pero este es el más rápido que he podido ejecutar.

+1

... resistiendo el impulso de etiquetar la pregunta con 'cat'. En serio, sin embargo, buen trabajo en la pregunta, muy detallado. – bernie

+0

He eliminado mi respuesta, ya que no iría a ningún lado. Si encuentras una manera de resolverlo, me gustaría que lo publicaras aquí y @mencioname para que pueda echarle un vistazo. – Tomalak

+0

@Tomalak: aquí hay una respuesta. – Quassnoi

Respuesta

3

Este es un problema general de elegir entre el filtrado y la ordenación de índice:

Usted debe mantener una lista de etiquetas populares (para los que el índice de pedidos es más beneficioso) y de alguna manera prohibir el índice de filtrado si la etiqueta es popular, digamos, así:

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 

Alternativamente, podría desnormalizar su esquema y agregar uploadedDate a TagBridge, llenándolo con un disparador o lo que sea. A continuación, crear un índice compuesto en TagBridge (pid_high, pid_low, uploadedDate, image_id_high, image_id_low) y volver a escribir la consulta un poco:

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; 

La doble sub consulta se debe a SQLite no entiende la sintaxis tupla.

+0

+1 La desnormalización funcionaría bastante bien. Se debe tener cuidado cuando se producen cambios de etiquetas para mantener todos los valores 'uploadloaded 'en sincronía, por supuesto. También me gusta la otra idea. – Tomalak

Cuestiones relacionadas