2009-04-07 28 views
18

Estoy teniendo problemas de rendimiento bastante importantes debido al uso de las declaraciones "ORDER BY" en mi código SQL.MySQL no usa índices ("Usando filesort") cuando usa ORDER BY

Todo está bien, siempre que no utilice ORDER BY-statements en el SQL. Sin embargo, una vez que introduzco ORDER BY: s en el código SQL todo se ralentiza dramáticamente debido a la falta de indexación correcta. Uno supondría que arreglar esto sería trivial, pero a juzgar por las discusiones en el foro, etc., esto parece ser un problema bastante común que todavía no he visto una respuesta definitiva y concisa a esta pregunta.

Pregunta: Dada la siguiente tabla ...

 
CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value2 (value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

... ¿Cómo se crean los índices que se utilizarán cuando se consulta la tabla para un valor1 -range al ordenar en el valor de valor2?

Actualmente, la recuperación está bien cuando NO se utiliza la cláusula ORDER BY.

consulte los siguientes EXPLIQUE salida de la consulta:

 
OK, when NOT using ORDER BY: 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
 
However, when using ORDER BY I get "Using filesort": 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra      | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where; Using filesort | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 

Alguna información adicional sobre el contenido de la tabla:

 
SELECT MIN(value1), MAX(value1) FROM values_table; 
+---------------+---------------+ 
| MIN(value1) | MAX(value2) | 
+---------------+---------------+ 
|    0 | 4294967295 | 
+---------------+---------------+ 

... 

SELECT MIN(value2), MAX(value2) FROM values_table; 
+---------------+---------------+ 
| MIN(value2) | MAX(value2) | 
+---------------+---------------+ 
|    1 |  953359 | 
+---------------+---------------+ 

Por favor, hágamelo saber si cualquier información adicional es necesaria para responder a la pregunta .

¡Muchas gracias de antemano!

Actualización # 1: Adición de un nuevo índice compuesto (ALTER TABLE ADD values_table ÍNDICE (valor1, valor2);) no resuelve el problema. Seguirá recibiendo "Uso de FileSort" después de agregar dicho índice.

Actualización n. ° 2: Una restricción que no mencioné en mi pregunta es que preferiría cambiar la estructura de la tabla (por ejemplo, agregar índices, etc.) que cambiar las consultas SQL utilizadas. Las consultas SQL se generan automáticamente usando Hibernate, así que considere aquellas más o menos corregidas.

+0

Supongo que quería decir valor1, valor2 en su actualización, ¿sí? – paxdiablo

+0

No importa, eso no funcionaría de todos modos debido a la explicación de @Quassnoi sobre el rango de valor1. Hubiera funcionado por un solo valor de valor1 pero no leí la pregunta lo suficientemente bien. La mejor de las suertes. – paxdiablo

+0

¿Utiliza los campos directamente en su consulta o está utilizando funciones? Como un campo de marca de tiempo y SEMANA (marca de tiempo). –

Respuesta

19

No puede usar un índice en este caso, ya que usa una condición de filtro RANGE.

Si desea usar algo como:

SELECT * 
FROM values_table this_ 
WHERE this_.value1 = @value 
ORDER BY 
     value2 
LIMIT 10 

, a continuación, crear un índice compuesto en (VALUE1, VALUE2) se utilizarían tanto para filtrar y para el pedido.

Pero usa una condición de rango, es por eso que tendrá que realizar pedidos de todos modos.

Su índice compuesto se verá así:

 
value1 value2 
----- ------ 
1  10 
1  20 
1  30 
1  40 
1  50 
1  60 
2  10 
2  20 
2  30 
3  10 
3  20 
3  30 
3  40 

, y si selecciona 1 y 2 en value1, que todavía no lo consigue todo un conjunto ordenado de value2.

Si su índice en value2 no es muy selectiva (.. I E no hay muchos DISTINCT value2 en la tabla), usted podría intentar:

CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1) 

/* Note the order, it's important */  

SELECT * 
FROM (
     SELECT DISTINCT value2 
     FROM mytable 
     ORDER BY 
       value2 
     ) q, 
     mytable m 
WHERE m.value2 >= q.value2 
     AND m.value2 <= q.value2 
     AND m.value1 BETWEEN 13123123 AND 123123123 

Esto se llama un método SKIP SCAN acceso. MySQL no lo admite directamente, pero se puede emular de esta manera.

El RANGE de acceso se utilizará en este caso, pero probablemente no obtendrá ninguna ventaja de rendimiento a menos DISTINCT value2 comprenden menos de aproximadamente 1% de filas.

Nota de uso de:

m.value2 >= q.value2 
AND m.value2 <= q.value2 

en lugar de

m.value2 = q.value2 

Esto hace MySQL realizar RANGE comprobando en cada bucle.

+0

+1 para resolver el problema de rango que perdí :-) – paxdiablo

+0

Gracias por su respuesta integral. Suponiendo que no puedo cambiar las consultas SQL utilizadas (las generadas automáticamente por Hibernate), ¿cree que es imposible de resolver (agregando decir mejor indexación)? – knorv

+0

Otra pregunta: si el problema es la consulta de rango, ¿cómo es que todo parece estar bien cuando no se usa ORDER BY? Lo siento si me perdí este detalle. – knorv

0

Me parece que tiene dos teclas totalmente independientes, una para value1 y otra para value2.

Por lo tanto, cuando usa la clave value1 para recuperar, los registros no se devuelven necesariamente en orden de valor2, por lo que deben ordenarse. Esto es aún mejor que una exploración de tabla completa ya que solo está ordenando los registros que satisfacen su cláusula "where value1".

Creo (si esto es posible en MySQL), una clave compuesta en (valor1, valor2) resolvería esto.

Probar:

CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value1and2 (value1,value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

(o el equivalente ALTER TABLE), suponiendo que esa es la sintaxis correcta en MySQL para una clave compuesta.

En todas las bases de datos que conozco (y debo admitir que MySQL no es una de ellas), el motor de DB seleccionaría la clave value1and2 para recuperar las filas y ya se ordenarían en value2-within- valor1, por lo que no necesitaría una clasificación de archivo.

Aún puede conservar la clave value2 si la necesita.

+0

Hola, gracias por su rápida respuesta. Probé la solución sugerida y, desafortunadamente, no funcionó. He agregado una aclaración a mi pregunta. – knorv

+0

No hay problemas, parece que @Quassnoi tiene más conocimiento de MySQL, así que te dejo en ello. Su explicación de por qué la ordenación es necesaria para los valores de rango 1 es algo que no recogí de la pregunta: DB2 tendría problemas similares. Remarcando como wiki comunitario para que nadie más cometa el mismo error. – paxdiablo

+0

Hay algún tipo de error SO que me impide marcar la pregunta community-wiki. – paxdiablo

Cuestiones relacionadas