2011-04-22 15 views
10

Estoy tratando de optimizar esta consulta:MySQL Query: presentar especie cuando combinación interna, límite y el orden por

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50; 

artículos de mesa:

  • Motor: MyISAM
  • ROW_FORMAT : Dinámico
  • Filas: 1 482 588
  • Data_length: 788 926 672
  • Max longitud de datos: 281 474 976 710 655
  • longitud Index: 127 300 608
  • de datos libre: 0
  • suma de comprobación: null
 CREATE TABLE `articles` (
     `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `title` VARCHAR(255) NOT NULL, 
    `publicationDate` DATE NOT NULL DEFAULT '1970-01-01', 
    PRIMARY KEY (`id`), 
    KEY `publicationDate` (`publicationDate`) 
    ) ENGINE=MYISAM AUTO_INCREMENT=1498496 DEFAULT CHARSET=utf8

articles_authors Tabla:

  • Motor: MyISAM
  • ROW_FORMAT: Dynamic
  • Filas: 1 970 750
  • Data_length: 45 008 420
  • Max longitud de datos: 281 474 976 710 655
  • longitud
  • Index: 127 300 608
  • de datos libre: 0
  • suma de comprobación: null
 CREATE TABLE `articles_authors` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `fk_Articles` int(10) unsigned NOT NULL, 
    `fk_Authors` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `fk_Articles_fk_Authors` (`fk_Articles`,`fk_Authors`), 
    KEY `fk_Articles` (`fk_Articles`), 
    KEY `fk_Authors` (`fk_Authors`), 
    ) ENGINE=MyISAM AUTO_INCREMENT=2349047 DEFAULT CHARSET=utf8

Explicar en consulta:

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref), possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors), KEY (fk_Authors), Key_len(4), ref(const), ROWS(171568), extra (USING TEMPORARY; USING FILE sort) 
id (1), select_type(SIMPLE), TABLE(articles), TYPE(eq_ref), possible_keys(PRIMARY), KEY (PRIMARY), Key_len(4), ref(articles_authors.fk_Authors), ROWS(1), extra() 

Como se puede ver, la consulta SQL no está optimizado (utilizando el archivo de clase de explicar).

Gracias por su ayuda!

+1

+1, ¡para una pregunta bien documentada! Me encanta cuando las personas realmente incluyen información pertinente! –

+0

No veo cómo esto puede optimizarse más, porque en la cláusula where/order tiene valores de dos tablas diferentes y no puede crear un índice compuesto '(fk_Authors, publicationDate)' – Pentium10

+0

Editado la respuesta para incluir un opción de des-normalización. – Johan

Respuesta

1

Es es usando el índice, como se dice en la explicación.

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref),
possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors),

KEY (fk_Authors), Key_len(4), ref(const), ROWS(171568),
extra (USING TEMPORARY; USING FILE sort)

Sólo como un adicional de las 50 filas que selecciona y que las órdenes por fecha de publicación es lo que hace un filesort.
Crea una tabla temporal con 50 elementos. Que luego ordena con tablesort.
Esto tiene por hacer de esta manera, porque MySQL no puede usar el gran índice en esos 50 elementos solitarios, costaría mucho en tiempo de acceso a IO.

Es más rápido ordenar hasta 50 números en la memoria y luego acceder al índice en el disco.

Usted puede hacer algo para acelerar la consulta sin embargo:

optimize table articles, articles_authors 

y vuelva a ejecutar la consulta.

EDIT: Acelerar la sugerencia de desnormalización artículos de mesa

Si vuelve a escribir la consulta como la siguiente:

SELECT articles.id FROM articles WHERE articles.id IN (
    SELECT articles_authors.fk_articles WHERE articles_authors.fk_authors = 586 
    LIMIT 0,50 
) 
ORDER BY articles.publicationDate; 

Es probable que vea el mismo rendimiento, pero pone de relieve el problema. Si el autor 586 tiene 180,000 artículos, entonces MySQL tiene que buscar 50 artículos de 180k en articles_authors y luego buscar 50 artículos de 180k nuevamente en la tabla de pedidos.

Si fusiona las mesas article_authors y artículos, los artículos de tabla se desnormalizado (suponiendo que un artículo puede tener múltiples autores) pero usted no tiene que hacer la unión y ahorrarse la segunda búsqueda.

CREATE TABLE `articles` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `publicationDate` date NOT NULL DEFAULT '1970-01-01', 
    `title` varchar(255) NOT NULL, 
    `fk_Authors` int(10) unsigned NOT NULL, 
PRIMARY KEY (`id`), 
UNIQUE KEY `Articles_fk_Authors` (`id`,`fk_Authors`), 
KEY `fk_Authors` (`fk_Authors`), 
KEY `publicationDate` (`publicationDate`) 
) ENGINE=MyISAM AUTO_INCREMENT=2349047 DEFAULT CHARSET=utf8 

Ahora puede seleccionar de ella al igual que

SELECT articles.id FROM articles WHERE articles.Author = 586 
ORDER BY articles.publicationDate LIMIT 50,0 
+0

Gracias por su respuesta. Ejecuto "optimizar artículos de tabla, articles_authors". Pero el problema de rendimiento aún aparece. Hay un autor que tiene 180 000 artículos. La consulta lleva más de 30 segundos y se notan grandes accesos de E/S. – heisenberg

0
SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles.id=586 
ORDER BY articles.publicationDate LIMIT 0,50; 
+0

Seleccionando el texto y presionando el botón '{}' formateará el código SQL por usted. Creo que cuatro espacios al comienzo de cada línea harán lo mismo –

+0

@Conrad: Eso es todo lo que '{}' hace ... pone 4 caracteres al comienzo de cada línea en el bloque de texto seleccionado. –

1

Tal vez esto le ayudará a:

SELECT articles.id 
    FROM articles 
     INNER JOIN (SELECT fk_Articles FROM articles_authors WHERE articles_authors.fk_Authors=586) sub ON articles.id=sub.fk_Articles 
ORDER BY articles.publicationDate LIMIT 0,50; 
0

No estoy seguro, pero la sugerencia de Conrad parece cambiar la clasificación y limitando, por lo que puede obtener los primeros 50 elementos de una lista aleatoria en orden en lugar de los primeros 50 elementos de una lista ordenada.

¿Podría una vista con la ayuda de combinación, si está ordenada por fk_author, publicationDate y tiene un índice? También depende de lo que está optimizando, la velocidad o el espacio en disco?

¿Se puede usar IN en Mysql? ¿Tal vez se optimice mejor? (código de ejemplo, no marcado)

SELECT id FROM articles WHERE id IN 
(SELECT fk_Articles FROM articles_authors WHERE fk_Authors=586) as IDs 
ORDER BY publicationDate LIMIT 0,50; 
0

Esto realmente puede ser válido, dependiendo de sus datos.

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50; 

Si articles_authors.fk_Authors = 586 conduce a filas razonablemente raras de acuerdo con las estadísticas recogidas por el motor de base de datos, que será más barato a buscar el todo y buscar a los 50 mejores filas.

Si, por el contrario, lleva a la mayoría de los artículos, será más económico consultar un índice en articles.publicationFecha y filtrar las filas no válidas hasta que tenga las 50 filas solicitadas.

Cuestiones relacionadas