2011-05-20 17 views
6

La base de datos es MySQL con el motor MyISAM.GRUPO POR optimización de consultas

definición de tabla:

CREATE TABLE IF NOT EXISTS matches (
    id int(11) NOT NULL AUTO_INCREMENT, 
    game int(11) NOT NULL, 
    user int(11) NOT NULL, 
    opponent int(11) NOT NULL, 
    tournament int(11) NOT NULL, 
    score int(11) NOT NULL, 
    finish tinyint(4) NOT NULL, 
    PRIMARY KEY (id), 
    KEY game (game), 
    KEY user (user), 
    KEY i_gfu (game , finish , user) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3149047 ; 

he puesto un índice en (game, finish, user) pero esta consulta GROUP BY todavía necesita 0,4 - 0,6 segundos para ejecutar:

SELECT user AS player 
    , COUNT(id) AS times 
FROM matches 
WHERE finish = 1 
    AND game = 19 
GROUP BY user 
ORDER BY times DESC 

La salida EXPLAIN:

| id | select_type | table | type | possible_keys | key | key_len | 
| 1 | SIMPLE  | matches | ref | game,i_gfu | i_gfu | 5 | 

| ref  | rows | Extra          | 
| const,const | 155855 | Using where; Using temporary; Using filesort | 

¿Hay alguna manera de hacerlo más rápido? La tabla tiene aproximadamente 800,000 registros.


EDIT: I cambió COUNT(id) en COUNT(*) y el tiempo se redujo a 0,08 - 0,12 segundos. Creo que lo he intentado antes de hacer el índice y olvidé cambiarlo de nuevo después.

En la salida explicar la mediante el índice de explica la aceleración:

| rows | Extra             | 
| 168029 | Using where; Using index; Using temporary; Using filesort | 

(pregunta al margen:? Es esta caída de un factor de 5 normal)

hay cerca de 2.000 usuarios, así que la clasificación final, incluso si usa filesort, no perjudica el rendimiento. Intenté sin ORDER BY y todavía toma casi el mismo tiempo.

+5

La razón por la que el recuento (\ *) tiene un rendimiento mucho más rápido que count (id) es que MySQL tiene una optimización específica para el caso de conteo (\ *). El caso de conteo (id) hace una segunda pasada a través de los datos para recuperar los resultados, donde el recuento (\ *) usa los contadores de fila internos existentes. Use conteo (\ *) siempre que sea posible. –

Respuesta

1

El EXPLAIN verifica que se usó el índice (game, finish, user) en la consulta. Ese parece ser el mejor índice posible para mí. ¿Podría ser un problema de hardware? ¿Cuál es la RAM y la CPU de tu sistema?

+0

La memoria es de 1GB. La CPU es (creo) AMD Opteron Quad-core 3.5GHz. –

+0

Supongo que su cuello de botella es la RAM. Yo sugeriría golpear eso a 4GB. – ic3b3rg

+0

4Gb para procesar la tabla con 900k filas ~ 30 bytes cada una? ;) Eso no es ni siquiera 30 mbytes;) – matt

7

Deshazte de la tecla 'juego' - es redundante con 'i_gfu'. Como 'id' es unique count (id) simplemente devuelve el número de filas en cada grupo, por lo que puede deshacerse de eso y reemplazarlo con count (*). Pruébalo de esa manera y pega la salida de EXPLAIN:

SELECT user AS player, COUNT(*) AS times 
FROM matches 
WHERE finish = 1 
AND game = 19 
GROUP BY user 
ORDER BY times DESC 
2

Eh, tough. Intente reordenar su índice: ponga primero la columna user (así que haga el índice (user, finish, game)) ya que eso aumenta la posibilidad de que GROUP BY pueda usar el índice. Sin embargo, en general, GROUP BY solo puede usar índices si limita las funciones agregadas utilizadas a MIN y MAX (consulte http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html y http://dev.mysql.com/doc/refman/5.5/en/loose-index-scan.html). Su pedido no está realmente ayudando tampoco.

+0

He intentado con ese índice y también '(usuario, juego, finalizar)' y forzar su uso, pero es aún más lento. –

+0

Impar Tengo la sensación de que no podrá mejorar con la combinación de GROUP BY y ORDER BY: es posible que desee crear una tabla agregada explícita si la velocidad de la consulta es demasiado lenta. El hecho de que * Using filesort * aparezca indica que el ORDER BY no pudo hacerse desde ningún índice: ¿quizás intente agregar el 'id' al índice? – Femi

+0

¿Te refieres a un índice '(juego, finalizar, usuario, identificación)'? –

1

Supongo que la mayor parte del tiempo se dedica a la extracción y, lo que es más importante, a la clasificación (dos veces, incluida la omitida al leer el índice) de 150k filas de 800k. Dudo que pueda optimizarlo mucho más de lo que ya es.

+0

Extrayendo, sí. Clasificando no, no pierde tiempo en ordenar. –

+0

Eso no es lo que sugiere su plan de consulta. Ni su consulta, para el caso. Ambos dicen que se necesita al menos un tipo. :-) –

+0

Quiero decir, el tiempo que pasa en la clasificación es muy corto en comparación con el tiempo dedicado a la agrupación. –

1

Como han señalado otros, es posible que haya alcanzado el límite de su capacidad para ajustar la consulta. A continuación, debe ver cuál es la configuración de las variables max_heap_table_size y tmp_table_size en su servidor. El valor predeterminado es 16 MB, que puede ser demasiado pequeño para su mesa.

+0

thnx para el consejo, ambas configuraciones están en 64M. –

1

Una de las deficiencias de esta consulta es que ordena por un agregado. Eso significa que no puede devolver ninguna fila hasta que se haya generado el conjunto de resultados completo; no puede existir ningún índice (para mysql myisam, de todos modos) para arreglar eso.

Sin embargo, puede desnormalizar sus datos bastante fácilmente para superar esto; Podría, por ejemplo, agregar un disparador de inserción/actualización para pegar un valor de conteo en una tabla de resumen, con un índice, para que pueda comenzar a devolver las filas de inmediato.

Cuestiones relacionadas