MySQL Indexing VarChar

10

Estoy tratando de indexar mi blogentriesbase de datos para un mejor rendimiento, pero encontré un problema.

Aquí está la estructura:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Una consulta como la siguiente utiliza el índice correctamente:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
El | id | select_type | mesa | tipo | posibles_claves | clave | key_len | ref | filas | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
El | 1 | SIMPLE | blogentries | indice | NULL | PRIMARIO | 114 NULL | 126 Usando index |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Sin embargo, cuando agrego el entry_iden la SELECTconsulta, usa el ordenamiento de archivos

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
El | id | select_type | mesa | tipo | posibles_claves | clave | key_len | ref | filas | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
El | 1 | SIMPLE | blogentries | TODOS | NULL | NULL | NULL | NULL | 126 Usando ordenar archivos |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Me preguntaba por qué sucede esto y cómo puedo evitarlo. ¿Se debe al VarChar, y eso debería cambiarse a otra cosa?

Estoy tratando de hacer que todas mis consultas usen el índice, ya que me encuentro con valores Handler_read_rndy Handler_read_rnd_nextvalores altos.

Si necesita alguna otra información, también puedo publicarla.


fuente
filesort significa que está realizando la ordenación en el disco.
Kermit
Intente agregar WHERE 1=1a su segunda consulta.
Kermit
¿Qué versión de MySQL es esta? ¿Cuál es el tamaño del búfer de clasificación ( SELECT @@sort_buffer_size)?
@njk filesort es el resultado de la parte 'ORDER BY' de la consulta
1
@TashPemhiwa No necesariamente, vea la primera declaración.
Kermit

Respuestas:

6

Como no tiene una WHEREcláusula en ninguna consulta, está devolviendo todas las filas en ambos casos, por lo que creo que el uso o no uso del índice tendría muy poco impacto en el rendimiento en estos ejemplos.

Joe Stefanelli
fuente
¿Seguramente MySQL debería usar el índice para el ORDER BY?
eggyal
@eggyal No si es demasiado grande para la memoria.
Kermit
@njk: Eso no tiene sentido ... puede atravesar el índice, en orden, sin necesidad de cargar todo en la memoria. Los resultados se ordenarían sin necesidad de realizar un ordenamiento de archivos.
eggyal
@eggyal, cuestionaría el tamaño de varchar(5000).
Kermit
@njk: Pero esa columna no está en el índice ni se usa en el orden.
eggyal
2

Como se documenta en ORDER BYOptimización :

Para consultas lentas para las que filesortno se utiliza, intente reducir max_length_for_sort_dataa un valor apropiado para activar a filesort.

En su artículo de blog ¿Qué es exactamente read_rnd_buffer_size? Peter Zaitsev explica:

Para mí, esto significa que desde MySQL 4.1 esta opción se usa en un rango estrecho de casos: si recupera pocos campos (menos de max_length_for_sort_data ), los datos deben almacenarse en el búfer de ordenación y el archivo de ordenación para que no haya necesidad de read_rnd_buffer, si las columnas seleccionadas son largos, por lo que son más largos que max_length_for_sort_data , con frecuencia significaría que hay algunas columnas TEXT / BLOB entre ellos. Sin embargo, se usaría si hay un gran número de columnas o si se usan columnas VARCHAR largas; solo se necesitan un par de UTF8 VARCHAR (255) para crear una fila que sea más larga que max_length_for_sort_data en su presentación estática.

Esto sugiere que max_length_for_sort_datahay un límite en el tamaño total de las columnas que uno está seleccionando, por encima del cual filesortse utilizará a en lugar de una clasificación basada en índices.

En su caso, la selección entry_id(5002 bytes) toma el tamaño total sobre el valor predeterminado de 1 KB de esta variable y, por filesortlo tanto, se utiliza. Para elevar el límite a 8 KB, puede hacer lo siguiente:

SET SESSION max_length_for_sort_data = 8192;
eggyal
fuente
Tengo una tabla con una configuración muy similar a esta, y esta configuración no parece desencadenar ningún cambio en el uso de ordenar archivos.
@muffinista: Eso es interesante. Supongo que podría estar relacionado con algunas de las otras configuraciones de búfer, según la respuesta de @ RolandoMySQLDBA .
eggyal
2

Has recibido muchas respuestas interesantes aquí, pero nadie ha respondido exactamente la pregunta: ¿por qué está sucediendo esto? Según tengo entendido, cuando una consulta SELECT contiene datos de longitud variable en MySQL, y no hay un índice que coincida con TODAS las columnas solicitadas, siempre usará un clasificador de archivos. El tamaño de los datos no es terriblemente relevante aquí. Es difícil encontrar una respuesta directa a esta pregunta en la documentación de MySQL, pero aquí hay una buena publicación de blog donde alguien está experimentando un problema muy similar al tuyo.

Consulte también: 10 consejos para optimizar las consultas de MySQL (que no apestan) .

Entonces, si es viable tener un índice en entry_id, entonces podría agregarlo y estar listo. Pero dudo que sea una opción, ¿qué hacer?

Si debe hacer algo al respecto es una pregunta aparte. Es importante saber que 'clasificador de archivos' está mal nombrado en MySQL : en realidad es solo el nombre del algoritmo utilizado para ordenar esta consulta en particular, y en muchos casos, el orden realmente sucederá en la memoria. Si no espera que esta tabla crezca mucho, probablemente no sea gran cosa.

Por otro lado, si esta tabla va a tener un millón de filas, es posible que tenga un problema. Si necesita admitir la paginación de consultas en esta tabla, entonces podría tener un problema de rendimiento realmente serio aquí. En ese caso, dividir sus datos de longitud variable en una nueva tabla y unir para recuperarlos es una optimización válida a tener en cuenta.

Aquí hay un par de otras respuestas sobre SO que hablan sobre esta pregunta:

Comunidad
fuente
La primera consulta del OP " contiene datos de longitud variable en MySQL, y no hay índice que coincida con TODAS las columnas solicitadas ", aunque filesortaparentemente no se utilizó en ese caso. También creo que incluso ordenar una tabla pequeña solo en la memoria podría ser un golpe de rendimiento inaceptable: por ejemplo, si la consulta se realiza mucho (y la tabla cambia para que no se puedan usar los cachés).
eggyal
No tengo tiempo para probarlo, pero me pregunto si esto se activa al tener un VARCHAR que requiere 2 bytes para almacenar la longitud como se especifica en dev.mysql.com/doc/refman/5.1/en/char. html : por lo tanto, la primera consulta se ajusta a ese límite pero la segunda no.
0

Intente agregar una WHEREcláusula a sus consultas.

El índice se puede usar incluso si ORDER BY no coincide exactamente con el índice, siempre que todas las partes no utilizadas del índice y todas las columnas ORDER BY adicionales sean constantes en la cláusula WHERE . En algunos casos, MySQL no puede usar índices para resolver ORDER BY , aunque todavía usa índices para encontrar las filas que coinciden con la cláusula WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


fuente
Pero en este caso ORDER BY , coincide exactamente con el índice, por lo que no es necesario tener una WHEREcláusula.
eggyal
Tengo una cláusula "where" en la consulta real en el sitio, así que sé que esa no es la causa del tipo de archivo. Me pregunto si es el uso de varchar?
0

Hasta donde yo sé, varchar solo puede contener un máximo de 8000 bytes, que son aproximadamente 4000 caracteres. Por lo tanto, 5000 parecería estar excediendo el límite de almacenamiento, y en este caso probablemente la razón por la cual la clasificación se está desordenando.

"varchar [(n | max)] Longitud variable, datos de caracteres no Unicode. n puede ser un valor de 1 a 8,000. max indica que el tamaño máximo de almacenamiento es 2 ^ 31-1 bytes. El tamaño de almacenamiento es el valor real longitud de los datos ingresados ​​+ 2 bytes. Los datos ingresados ​​pueden tener una longitud de 0 caracteres. Los sinónimos SQL-2003 para varchar varían en caracteres o varían en caracteres ".

Espero que esto responda a su pregunta


fuente
Tal como se documenta en The CHARand VARCHARtypes : "Los valores en las columnas VARCHAR son cadenas de longitud variable. La longitud se puede especificar como un valor de 0 a 255 antes de MySQL 5.0.3 y de 0 a 65.535 en 5.0.3 y versiones posteriores. la longitud máxima de a VARCHARen MySQL 5.0.3 y posterior está sujeta al tamaño máximo de fila (65.535 bytes, que se comparte entre todas las columnas) y al conjunto de caracteres utilizado. "
eggyal
0

Solo tiene 126 filas en su tabla. Incluso si cada fila tiene un tamaño máximo de aproximadamente 5 KB, eso significaría que el tamaño total para leer desde el disco es solo de aproximadamente 600 KB, esto no es mucho. Para ser sincero, es una cantidad muy pequeña, probablemente menor que el tamaño de caché de la mayoría de las unidades de disco modernas.

Ahora, si el servidor necesita recuperar sus datos para cumplir con su consulta, la operación más costosa es leerlos desde el disco. Pero, leerlo de acuerdo con el orden del índice NO siempre es la forma más rápida de hacerlo, especialmente cuando la cantidad de datos es tan pequeña.

En su caso, es MUCHO más eficiente leer datos de la tabla completa del disco como un solo bloque en la memoria (probablemente en una sola operación de lectura o búsqueda de disco), y luego ordenarlo en RAM para satisfacer ORDER BY, que es instantáneo en comparación con el disco operación de lectura. Si el servidor lee sus datos de acuerdo con el índice, tendría que emitir hasta 126 (¡Uy!) Operaciones de lectura, buscando de ida y vuelta dentro del mismo archivo de datos muchas veces.

En otras palabras, la exploración secuencial NO siempre es algo malo, y mysql no es necesariamente estúpido. Si intenta forzar a mysql a usar ese índice, lo más probable es que funcione más lento que el análisis secuencial que tiene actualmente.

Y la razón por la que estaba usando el índice cuando no se incluyó el campo de 5 KB es porque los datos recuperados no constituían el 99% de los datos en la tabla. Cuando incluyó su campo de 5 KB, ahora la consulta tiene que leer el 99% de los datos, y es más barato leerlo todo y luego ordenarlo en la memoria.

mvp
fuente
Parece que está confundiendo varias cosas de Cómo evitar los escaneos de tabla completa , que tienen que ver con el uso del índice en JOINcondiciones y WHEREcláusulas satisfactorias , no ORDER BYcláusulas.
eggyal
Exactamente lo contrario. En este caso particular, el escaneo completo de la tabla es BUENO simplemente porque es MÁS RÁPIDO que leer por orden de índice.
0

¿Qué versión de MySQL estás usando?

EN 5.1, traté de configurar su escenario y llené algunos datos ficticios. Usando los SQL que proporcionó, solo obtengo un escaneo de tabla cada vez de acuerdo con EXPLAIN. Por defecto, cuando usa el orden por MYSQL, recurre al ordenamiento de archivos incluso si el índice primario se usa en el orden por.


fuente