¿Cuándo debo usar un índice compuesto?

133
  1. ¿Cuándo debo usar un índice compuesto en una base de datos?
  2. ¿Cuáles son las ramificaciones de rendimiento al usar un índice compuesto)?
  3. ¿Por qué debería usar un índice compuesto?

Por ejemplo, tengo una homestabla:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

¿Tiene sentido para mí usar un índice compuesto para ambos geolaty geolng, de modo que:

Sustituyo:

  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),

con:

KEY `geolat_geolng` (`geolat`, `geolng`)

Si es así:

  • ¿Por qué?
  • ¿Cuál es la ramificación del rendimiento al usar un índice compuesto)?

ACTUALIZAR:

Dado que muchas personas lo han declarado totalmente dependiente de las consultas que realizo, a continuación es la consulta más común realizada:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

ACTUALIZACIÓN 2:

Con el siguiente esquema de base de datos:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `primary_photo_group_id` int(10) unsigned NOT NULL default '0',
  `customer_id` bigint(20) unsigned NOT NULL,
  `account_type_id` int(11) NOT NULL,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `num_of_beds` tinyint(3) unsigned NOT NULL,
  `num_of_baths` decimal(3,1) unsigned NOT NULL,
  `num_of_floors` tinyint(3) unsigned NOT NULL,
  `description` text collate utf8_unicode_ci,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  `display_status` tinyint(1) NOT NULL,
  `date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
  `contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`home_id`),
  KEY `customer_id` (`customer_id`),
  KEY `city` (`city`),
  KEY `num_of_beds` (`num_of_beds`),
  KEY `num_of_baths` (`num_of_baths`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
  KEY `account_type_id` (`account_type_id`),
  KEY `display_status` (`display_status`),
  KEY `sqft` (`sqft`),
  KEY `price` (`price`),
  KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;

Usando el siguiente SQL:

EXPLAIN SELECT  homes.home_id,
                    address,
                    city,
                    state,
                    zip,
                    price,
                    sqft,
                    year_built,
                    account_type_id,
                    num_of_beds,
                    num_of_baths,
                    geolat,
                    geolng,
                    photo_id,
                    photo_url_dir
            FROM homes
            LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
                AND homes.primary_photo_group_id = home_photos.home_photo_group_id
                AND home_photos.home_photo_type_id = 2
            WHERE homes.display_status = true
            AND homes.geolat BETWEEN -100 AND 100
            AND homes.geolng BETWEEN -100 AND 100

EXPLICAR devuelve:

id  select_type  table        type  possible_keys                                    key                  key_len  ref     rows  Extra
----------------------------------------------------------------------------------------------------------
1   SIMPLE       homes        ref   geolat,geolng,display_status                     display_status       1        const   2     Using where
1  SIMPLE        home_photos  ref   home_id,home_photo_type_id,home_photo_group_id   home_photo_group_id  4        homes.primary_photo_group_id   4  

No entiendo cómo leer el comando EXPLAIN. ¿Esto se ve bien o mal? En este momento, NO estoy usando un índice compuesto para geolat y geolng. ¿Debo ser?

Osito de peluche
fuente

Respuestas:

111

Debe usar un índice compuesto cuando use consultas que se beneficien de él. Un índice compuesto que se ve así:

index( column_A, column_B, column_C )

beneficiará una consulta que use esos campos para unir, filtrar y, a veces, seleccionar. También beneficiará las consultas que utilizan subconjuntos de columnas del extremo izquierdo de ese compuesto. Por lo tanto, el índice anterior también satisfará las consultas que necesiten

index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )

Pero no será (al menos no directamente, tal vez pueda ayudar parcialmente si no hay mejores índices) ayuda para consultas que necesitan

index( column_A, column_C )

Observe cómo falta column_B.

En su ejemplo original, un índice compuesto para dos dimensiones beneficiará principalmente las consultas que consultan en ambas dimensiones o en la dimensión más a la izquierda por sí mismo, pero no en la dimensión más a la derecha por sí misma. Si siempre consulta dos dimensiones, un índice compuesto es el camino a seguir, no importa cuál sea el primero (lo más probable).

Mark Canlas
fuente
1
Mark, he actualizado mi publicación original (actualización 2). Esta es mi consulta real. Mi esquema de base de datos real. Y lo que devuelve el comando EXPLAIN. Entonces, con esta información, ¿debería estar usando un índice compuesto? Todavía no estoy claro. Gracias por adelantado.
Teddy
Mark, ¿el índice compuesto en su respuesta satisface el índice (column_C)?
Boris D. Teoharov
No estoy seguro de entender tu pregunta. Pero, si está preguntando si el índice (A, B, C) ayudaría a una consulta que se filtra en la columna C, la respuesta generalmente sería no, no usaría el índice para el filtrado. Sin embargo, podría usar el índice para eliminar un escaneo de tabla si solo selecciona un subconjunto de ABC. Entonces, eso es diferente, pero relacionado. Pero para los usos típicos de los índices para habilitar el filtrado, la respuesta es no.
Mark Canlas
1
-1 porque un índice compuesto no ayuda con WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???. Se detendrá después del primer campo. La respuesta de "Desbordamiento de preguntas" explica por qué.
Rick James
1
@felwithe MySQL solo puede usar un índice por cada una de las tablas en una consulta (las exenciones están ahí, por ejemplo, fusión de índices). Lo que idealmente significa que una tabla en una consulta, debe usar un índice único para todas las cláusulas where, table join, group-by y order-by. Por lo tanto, un índice separado en cada columna puede no funcionar siempre, pero un índice compuesto puede hacer la magia.
AKHIL MATHEW
56

Imagine que tiene las siguientes tres consultas:

Consulta I:

SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4

Consulta II:

SELECT * FROM homes WHERE `geolat`=42.9

Consulta III:

SELECT * FROM homes WHERE `geolng`=36.4

Si tiene un índice separado por columna, las tres consultas usan índices. En MySQL, si tiene un índice compuesto ( geolat, geolng), solo la consulta I y la consulta II (que usa la primera parte del índice compuesto) usa índices. En este caso, la consulta III requiere una búsqueda completa en la tabla.

En la sección de índices de columnas múltiples del manual, se explica claramente cómo funcionan los índices de columnas múltiples, por lo que no quiero volver a escribir el manual.

Desde la página del Manual de referencia de MySQL :

Un índice de varias columnas puede considerarse una matriz ordenada que contiene valores que se crean concatenando los valores de las columnas indexadas .

Si usa un índice separado para las columnas geolat y geolng, tiene dos índices diferentes en su tabla que puede buscar de forma independiente.

INDEX geolat
-----------
VALUE RRN
36.4  1
36.4  8
36.6  2
37.8  3
37.8  12
41.4  4

INDEX geolng
-----------
VALUE RRN
26.1  1
26.1  8
29.6  2
29.6  3
30.1  12
34.7  4

Si usa un índice compuesto, solo tiene un índice para ambas columnas:

INDEX (geolat, geolng)
-----------
VALUE      RRN
36.4,26.1  1
36.4,26.1  8
36.6,29.6  2
37.8,29.6  3
37.8,30.1  12
41.4,34.7  4

RRN es un número de registro relativo (para simplificar, puede decir ID). Los dos primeros índices generados se separan y el tercer índice es compuesto. Como puede ver, puede buscar en base a geolng en uno compuesto ya que está indexado por geolat, sin embargo, es posible buscar por geolat o "geolat AND geolng" (ya que geolng es un índice de segundo nivel).

Además, eche un vistazo a la sección del manual Cómo MySQL usa los índices .

Emre Yazici
fuente
1
En realidad, no tengo ninguna de esas consultas. Mi consulta aparece en la publicación original. Mi consulta es regresar a las casas dentro de una cuadrícula cuadrada. Sé sobre espacial y no estoy tratando de calcular distancias. Simplemente quiero saber si usar un índice compuesto tiene sentido cuando intento mostrar todas las casas dentro de una cuadrícula geográfica particular (por ejemplo, vecindario / ciudad / condado)
Teddy
Eyazici, he actualizado mi publicación original (actualización 2). Esta es mi consulta real. Mi esquema de base de datos real. Y lo que devuelve el comando EXPLAIN. Entonces, con esta información, ¿debería estar usando un índice compuesto? Todavía no estoy claro. Gracias de antemano
Teddy
@ "En realidad, no tengo ninguna de esas consultas". En realidad sí, he usado la condición WHERE simple para explicar la lógica base. Cuando se usa un condicional (es decir, DÓNDE) en una columna, MySQL intenta usar índices siempre que sea posible. "x ENTRE a AND b" es similar a "x> a AND x <b". Ha utilizado las columnas geolng y geolat en su consulta condicional. Si utiliza el índice de composición "(geolat, geolng)" su "AND geolng ENTRE ??? AND ???" condicional no obtiene ventajas de índice (esto es para MySQL). Por lo tanto, debe usar un índice separado por columna para su escenario.
Emre Yazici
No entiendo. ¿Por qué debería usar índices separados para geolat y geolng cuando SIEMPRE realizaré una consulta que incluya ambas columnas
Teddy
1
No. Cuando se encuentra un "rango" (como con BETWEEN), ¡no se consideran más campos del índice! Entonces el índice compuesto no es mejor.
Rick James
19

Podría haber una idea errónea sobre lo que hace el índice compuesto. Mucha gente piensa que el índice compuesto se puede utilizar para optimizar una consulta de búsqueda siempre que la wherecláusula cubra las columnas indexadas, en su caso geolaty geolng. Profundicemos más:

Creo que sus datos sobre las coordenadas de las casas serían decimales aleatorios como tales:

home_id  geolat  geolng
   1    20.1243  50.4521
   2    22.6456  51.1564
   3    13.5464  45.4562
   4    55.5642 166.5756
   5    24.2624  27.4564
   6    62.1564  24.2542
...

Como geolaty los geolngvalores apenas se repiten. Un índice compuesto geolaty geolngse vería así:

index_id  geolat  geolng
   1     20.1243  50.4521
   2     20.1244  61.1564
   3     20.1251  55.4562
   4     20.1293  66.5756
   5     20.1302  57.4564
   6     20.1311  54.2542
...

¡Por lo tanto, la segunda columna del índice compuesto es básicamente inútil ! La velocidad de su consulta con un índice compuesto probablemente será similar a un índice solo en la geolatcolumna.

Como lo mencionó Will, MySQL proporciona soporte de extensión espacial . Un punto espacial se almacena en una sola columna en lugar de dos lat lngcolumnas separadas . El índice espacial se puede aplicar a dicha columna. Sin embargo, la eficiencia podría ser sobrevalorada en base a mi experiencia personal. Podría ser que el índice espacial no resuelva el problema bidimensional, sino que simplemente acelere la búsqueda usando R-Trees con división cuadrática .

La compensación es que un punto espacial consume mucha más memoria, ya que utiliza números de doble precisión de ocho bytes para almacenar coordenadas. Corrígeme si estoy equivocado.

Desbordamiento de preguntas
fuente
5

Los índices compuestos son muy potentes ya que:

  • Hacer cumplir la integridad de la estructura
  • Habilitar la clasificación en una ID FILTRADA

INTEGRIDAD DE LA ESTRUCTURA DE APLICACIÓN

Los índices compuestos no son simplemente otro tipo de índice; pueden proporcionar la estructura NECESARIA a una tabla al hacer cumplir la integridad como la Clave primaria.

Innodb de Mysql admite la agrupación y el siguiente ejemplo ilustra por qué un índice compuesto puede ser necesario.

Para crear un grupo de amigos tablas (es decir, para una red social) necesitamos 2 columnas: user_id, friend_id.

Tabla Strcture

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

En virtud, una Clave primaria (PK) es única y al crear una PK compuesta, Innodb verificará automáticamente que no existan duplicados user_id, friend_idcuando se agrega un nuevo registro. Este es el comportamiento esperado ya que ningún usuario debería tener más de 1 registro (enlace de relación) con, friend_id = 2por ejemplo.

Sin un PK compuesto, podemos crear este esquema usando una clave sustituta:

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

Ahora, cada vez que se agrega un nuevo registro, tendremos que verificar que user_id, friend_idno exista un registro anterior con la combinación .

Como tal, un índice compuesto puede hacer cumplir la integridad de la estructura.

HABILITAR CLASIFICACIÓN EN UNA ID FILTRADA

Es muy común ordenar un conjunto de registros por la hora de la publicación (marca de tiempo o fecha y hora). Por lo general, esto significa publicar en una identificación determinada. Aquí hay un ejemplo

Table User_Wall_Posts (piense si las publicaciones en el muro de Facebook)

user_id (medium_int)
timestamp (timestamp)
author_id (medium_int)
comment_post (text)

Primary Key -> (user_id, timestamp, author_id)

Queremos consultar y encontrar todas las publicaciones user_id = 10y ordenar las publicaciones de comentarios por timestamp(fecha).

Consulta SQL

SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES

El PK compuesto le permite a Mysql filtrar y ordenar los resultados usando el índice; Mysql no tendrá que usar un archivo temporal u ordenar archivos para obtener los resultados. Sin una clave compuesta, esto no sería posible y causaría una consulta muy ineficiente.

Como tal, las claves compuestas son muy potentes y se adaptan más que el simple problema de "Quiero buscar, column_a, column_basí que usaré claves compuestas. Para mi esquema de base de datos actual, tengo tantas claves compuestas como claves individuales. No pase por alto uso de una clave compuesta!

ProfileTwist
fuente
5

Los índices compuestos son útiles para

  • 0 o más cláusulas "=", más
  • a lo sumo una cláusula de rango.

Un índice compuesto no puede manejar dos rangos. Discuto esto más a fondo en mi libro de cocina índice .

Encuentre el más cercano : si la pregunta es realmente sobre la optimización

WHERE geolat BETWEEN ??? AND ???
  AND geolng BETWEEN ??? AND ???

entonces ningún índice realmente puede manejar ambas dimensiones.

En cambio, uno debe "pensar fuera de la caja". Si una dimensión se implementa a través de la partición y la otra se implementa seleccionando cuidadosamente PRIMARY KEY, se puede obtener una eficiencia significativamente mejor para tablas muy grandes de búsqueda de lat / lng. Mi último blog detalla cómo implementar "encontrar el más cercano" en el mundo. Incluye codigo.

El PARTITIONsson franjas de rangos de latitud. El PRIMARY KEYdeliberadamente comienza con la longitud, por lo que es probable que las filas útiles estén en el mismo bloque. Una rutina almacenada organiza el código desordenado para hacer order by... limit...y para hacer crecer el 'cuadrado' alrededor del objetivo hasta que tenga suficientes cafeterías (o lo que sea). También se encarga de los cálculos de gran círculo y el manejo de la línea de fecha y los polos.

Más

He escrito otro blog; compara 5 formas de realizar búsquedas lat / lng: http://mysql.rjweb.org/doc.php/latlng#representation_choices (hace referencia al enlace anterior como una de las 5.) Una de las otras formas es esta, y señala que son óptimas para el caso particular :

INDEX(geolat, geolng),
INDEX(geolng, geolat)

Es decir, tener ambas columnas en dos índices y no tener índices de una sola columna en geolat y geolng es importante.

Rick James
fuente
1

No hay respuesta en blanco y negro, talla única.

Debe usar un índice compuesto, cuando su carga de trabajo de consulta se beneficiaría de uno.

Debe determinar su carga de trabajo de consulta para determinar esto.

Un índice compuesto entra en juego cuando las consultas pueden satisfacerse por completo desde ese índice.

ACTUALIZACIÓN (en respuesta a la edición a la pregunta publicada): si está seleccionando * de la tabla, puede usarse el índice compuesto, puede que no. Deberá ejecutar EXPLAIN PLAN para estar seguro.

Trigo Mitch
fuente
¿Tiene sentido usar un índice compuesto para datos de ubicación geográfica (latitud y longitud)?
Teddy
1
Depende completamente de qué consultas se realicen en esa tabla.
Mitch Wheat
He actualizado mi publicación original para incluir la consulta más común realizada. Véase más arriba.
Teddy
1

Para realizar búsquedas espaciales, necesita un algoritmo R-Tree , que permite buscar áreas geográficas muy rápidamente. Exactamente lo que necesitas para este trabajo.

Algunas bases de datos tienen índices espaciales incorporados. Una búsqueda rápida en Google muestra que MySQL 5 los tiene (que al mirar su SQL, supongo que está usando MySQL).

Será
fuente
1

El índice compuesto puede ser útil cuando desea optimizar la group bycláusula (consulte este artículo http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html ). Por favor pon atención:

Las condiciones previas más importantes para usar índices para GROUP BY son que todas las columnas GROUP BY hacen referencia a atributos del mismo índice y que el índice almacena sus claves en orden (por ejemplo, este es un índice BTREE y no un índice HASH)

Alejandro
fuente
GROUP BYNo fue mencionado.
Rick James
¿No se mencionó dónde? :) Obviamente se menciona en el artículo al que me referí. Y responde a las preguntas que se hicieron: ¿Cuándo debo usar un índice compuesto en una base de datos? ¿Cuáles son las ramificaciones de rendimiento mediante el uso de un índice compuesto)? ¿Por qué debería usar un índice compuesto?
Alexander
Corrección: GROUP BYno fue mencionado por el OP.
Rick James
Claro, esa fue la respuesta, uno de los casos en los que usaríamos un índice compuesto en una base de datos.
Alexander
0

Estoy con @Mitch, depende completamente de tus consultas. Afortunadamente, puede crear y soltar índices en cualquier momento, y puede anteponer la palabra clave EXPLAIN a sus consultas para ver si el analizador de consultas utiliza los índices.

Si está buscando un par lat / long exacto, este índice probablemente tenga sentido. Pero probablemente va a buscar casas a cierta distancia de un lugar en particular, por lo que sus consultas se verán así (ver fuente ):

select *, sqrt(  pow(h2.geolat - h1.geolat,  2) 
               + pow(h2.geolng - h1.geolng, 2) ) as distance
from homes h1, homes h2
where h1.home_id = 12345 and h2.home_id != h1.home_id
order by distance

y es muy probable que el índice no sea útil en absoluto. Para consultas geoespaciales, necesita algo como esto .

Actualización: con esta consulta:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

El analizador de consultas podría usar un índice solo en geolat, o un índice solo en geolng, o posiblemente ambos índices. No creo que use un índice compuesto. Pero es fácil probar cada una de estas permutaciones en un conjunto de datos real y luego (a) ver lo que EXPLAIN le dice y (b) medir el tiempo que realmente lleva la consulta.

Jim Ferrans
fuente
Simplemente estoy usando querer regresar a las casas dentro de una cuadrícula cuadrada. Sé sobre espacial, así que no estoy tratando de calcular la distancia. Simplemente quiero regresar a los hogares dentro de la cuadrícula cuadrada y quiero que funcione rápidamente. Como tal, quiero asegurarme de tener mis índices configurados correctamente. ¿Eso ayuda?
Teddy