2010-08-22 15 views
5

Suposse que tienen la siguiente base de datos MySQL con 500.000 filas:Optimizar esta consulta, recuperar a los usuarios de una base de datos MySQL con 500.000 usuarios y uno condicional

users 
{ 
    id  - int, 
    name  - varchar(32), 
    verified - tinyint(1) 
} 

primary { id } 
index { verified } 

Y necesitan para obtener últimos 20 usuarios no verificados, así que utilice la siguiente consulta:

SELECT * FROM users WHERE verified != 1 ORDER BY id DESC LIMIT 20 

pero se necesita 1,2 segundos para completar.

¿Cómo puedo optimizarlo? O obtenga el mismo resultado de otra manera en php.

[EDIT]

ID es el índice principal, VERIFICADO es un índice demasiado

[EDIT 2]

CREATE TABLE `users` (
    `id` int(10) unsigned NOT NULL auto_increment COMMENT 'Identificador del usuario', 
    `login` varchar(32) NOT NULL COMMENT 'Login para entrar', 
    `password` varchar(32) NOT NULL COMMENT 'Password para entrar', 
    `email` varchar(384) NOT NULL COMMENT 'Email del usuario', 
    `group_id` int(10) unsigned default NULL, 
    `display_name` varchar(64) NOT NULL COMMENT 'Nombre para mostrar', 
    `email_verified` tinyint(3) unsigned default '0' COMMENT 'Email verificado?', 
    `banned` tinyint(3) unsigned default '0' COMMENT 'Baneado?', 
    `admin` tinyint(3) unsigned default '0' COMMENT 'Es un super administrador del sitio?', 
    `registered` int(10) unsigned NOT NULL COMMENT 'Fecha del registro', 
    PRIMARY KEY (`id`), 
    KEY `login` (`login`), 
    KEY `password` (`password`), 
    KEY `email` (`email`(333)), 
    KEY `group_id` (`group_id`), 
    KEY `email_verified` (`email_verified`), 
    KEY `banned` (`banned`), 
    KEY `admin` (`admin`), 
    KEY `registered` (`registered`) 
) ENGINE=MyISAM AUTO_INCREMENT=500002 DEFAULT CHARSET=utf8; 

[EDIT 3]

EXPLAIN(SELECT id FROM users WHERE email_verified != 1 ORDER BY id DESC LIMIT 20) 

es

id: 1 
select_type: SIMPLE 
table: users  
type: range 
possible_keys: email_verified 
key: email_verified 
key_len: 2  
ref: 
rows: 345195  
Extra: Using where; Using filesort 

Y un perfil de la consulta:

Status Duration 
(initialization) 0.0000307 
Opening tables 0.000003 
System lock 0.0000017 
Table lock 0.0000042 
init 0.000017 
optimizing 0.0000077 
statistics 0.000097 
preparing 0.000054 
executing 0.0000007 
Sorting result 1.2321507 
Sending data 0.000272 
end 0.000004 
query end 0.0000025 
freeing items 0.0000099 
closing tables 0.0000025 
logging slow query 0.0000005 
+0

Eso debería tomar como 0 segundos para calcular. ¿Estás seguro de que tienes el índice verificado y no (id, verificado)? – Johan

+0

@Johan: clave primaria en ID y una LLAVE simple en VERIFICADO, lo que tomó más tiempo es VERIFICADO! = 1 – Wiliam

+0

¿Puede envolver la consulta en un 'EXPLAIN()' y publicar los resultados? –

Respuesta

3

Hace falta un índice que abarca tanto id y email_verfied

KEY `foo` (`id`,`email_verified`) 

Entonces EXPLAIN(SELECT id FROM users WHERE email_verified != 1 ORDER BY id DESC LIMIT 20) impresiones

+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | users | index | email_verified | Index 4 | 6  | NULL | 5 | Using where; Using index | 
+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 

Especialmente el lento Using filesort se ha ido. respuesta

+0

Ahora es una segunda consulta secundaria :) ¿Por qué funcionó? ¿Cómo funciona este índice con 2 columnas? – Wiliam

+0

AH, esto solo funciona si elimina el índice anterior "email_verified". – Wiliam

+0

esto es fascinante. pero contradice completamente mi intuición sobre cómo funcionan los índices. Volker, ¿puedes explicar por qué dos índices separados no lo hicieron? – Nicolas78

0

añadir un índice en verified, y utilizar la consulta siguiente en su lugar:

SELECT * FROM users WHERE verified = 0 ORDER BY id DESC LIMIT 20 

(asumiendo Ca verificado n sea 0 o 1).

Si usa <> en lugar de = MySQL ignorará los índices y realizará un escaneo completo de la tabla, lo que ralentizará su consulta de forma espectacular.

+0

mismo resultado con = 0, 1.2 segundos – Wiliam

1

Agregue un nuevo índice: {verified, id}. de lo contrario, tendrá que hacer un escaneo completo de la tabla.

+0

Hará un escaneo completo de la tabla, independientemente de si mantiene el operador '<>'. – quantumSoup

+0

Esta fue una solución válida, pero no lo he entendido por primera vez. Gracias. – Wiliam

+0

@quantumSoup: Funciona con <> – Wiliam

0

Editar: Su consulta es lenta cuando ordena el resultado; esto puede ser causado por el índice si el orden dado es incorrecto. MySQL se establece por defecto en el orden de clasificación de ASC.

Ver manual de MySQL: http://dev.mysql.com/doc/refman/5.0/en/create-index.html

An index_col_name specification can end with ASC or DESC. These keywords are permitted for future extensions for specifying ascending or descending index value storage. Currently, they are parsed but ignored; index values are always stored in ascending order. 

Por favor, compruebe los siguientes enlaces:

http://www.ozzu.com/programming-forum/mysql-index-cardinality-t71876.html

Why does the cardinality of an index in MySQL remain unchanged when I add a new index?

Es posible que necesite para optimizar su índice:

OPTIMIZE TABLE users; 

Podría tener sentido tener un índice con identificación y verificado combinado.

Tiene muchos índices, esto podría confundir a mysql.

+0

OPTIMIZE no funcionó, por lo tanto la cardinalidad del índice verificado es 2 – Wiliam

+0

Ah, hay muchos índices porque los uso todos. – Wiliam

0

Haga otra tabla sin usuarios verificados. Esta es la última solución que quiero usar.

0

igual de quantumSoup, sino que tiene un índice en (verificado, id) (asumiendo que usted está asumiendo id se para siempre en aumento)

SELECT * FROM users WHERE verified = 0 ORDER BY id DESC LIMIT 20 

También estoy asumiendo que se verifica es sólo 0,1

No use <> ya que eso derrota el índice. Ponga "verificado" primero en el índice para que pueda hacer un escaneo de rango simple.

+0

¿Por qué VERIFICADO antes de la identificación? – Wiliam

+0

Porque desea hacer que la primera parte del índice sea la expresión constante (= 0) para que pueda hacer un escaneo de rango. – MarkR

0

Si únicos valores posibles para verificadas son 0 y 1, crear un índice en

(verified, id) 

y volver a escribir su consulta:

SELECT * 
FROM users 
WHERE verified = 0 
ORDER BY 
     id DESC 
LIMIT 20 

Esta solución utiliza un índice único tanto para el filtrado y para el pedido (y funciona bien independientemente de su distribución de datos).

Cuestiones relacionadas