Quand dois-je utiliser un index composite?

133
  1. Quand dois-je utiliser un index composite dans une base de données?
  2. Quelle est la ramification des performances en utilisant un index composite)?
  3. Pourquoi devrais-je utiliser un index composite?

Par exemple, j'ai une homestable:

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  ;

Est-il judicieux pour moi d'utiliser un index composite pour les deux geolatet geolng, tel que:

Je remplace:

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

avec:

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

Si c'est le cas:

  • Pourquoi?
  • Quelle est la ramification des performances en utilisant un index composite)?

METTRE À JOUR:

Étant donné que de nombreuses personnes l'ont déclaré entièrement dépendant des requêtes que j'effectue, voici la requête la plus courante effectuée:

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

MISE À JOUR 2:

Avec le schéma de base de données suivant:

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 ;

En utilisant le SQL suivant:

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

EXPLAIN renvoie:

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  

Je ne comprends pas très bien comment lire la commande EXPLAIN. Cela semble-t-il bon ou mauvais. À l'heure actuelle, je n'utilise PAS un index composite pour geolat et geolng. Dois-je l'être?

Teddy
la source

Réponses:

111

Vous devez utiliser un index composite lorsque vous utilisez des requêtes qui en bénéficient. Un index composite qui ressemble à ceci:

index( column_A, column_B, column_C )

bénéficiera d'une requête qui utilise ces champs pour joindre, filtrer et parfois sélectionner. Cela profitera également aux requêtes qui utilisent les sous-ensembles de colonnes les plus à gauche dans ce composite. Ainsi, l'index ci-dessus satisfera également les requêtes qui nécessitent

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

Mais cela n'aidera pas (du moins pas directement, peut-être que cela peut aider partiellement s'il n'y a pas de meilleurs index) pour les requêtes qui ont besoin

index( column_A, column_C )

Remarquez comment column_B est manquant.

Dans votre exemple d'origine, un index composite pour deux dimensions bénéficiera principalement aux requêtes qui interrogent les deux dimensions ou la dimension la plus à gauche en elle-même, mais pas la dimension la plus à droite en elle-même. Si vous interrogez toujours deux dimensions, un index composite est la voie à suivre, peu importe lequel est le premier (très probablement).

Mark Canlas
la source
1
Mark, j'ai mis à jour mon message d'origine (mise à jour 2). Ceci est ma requête réelle. Mon schéma de base de données actuel. Et ce que la commande EXPLAIN renvoie. Donc, avec cette information - devrais-je utiliser un index composite. Je ne suis toujours pas clair. Merci d'avance.
Teddy
Mark, est-ce que l'index composite de votre réponse satisfait l'index (column_C)?
Boris D. Teoharov
Je ne suis pas sûr de comprendre votre question. Mais, si vous demandez si l'index (A, B, C) aiderait une requête qui filtre sur la colonne C, la réponse serait généralement non, il n'utiliserait pas l'index pour le filtrage. Il pourrait cependant utiliser l'index pour éliminer une analyse de table si vous ne sélectionnez que sur un sous-ensemble d'ABC. Donc, c'est différent, mais lié. Mais pour les utilisations typiques des index pour activer le filtrage, la réponse est non.
Mark Canlas
1
-1 car un index composite n'aide pas avec WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???. Il s'arrêtera après le premier champ. La réponse de "Question Overflow" explique pourquoi.
Rick James
1
@felwithe MySQL ne peut utiliser qu'un seul index pour chacune des tables d'une requête (des exceptions existent. Par exemple, la fusion d'index). Ce qui signifie idéalement qu'une table dans une requête doit utiliser un seul index pour toutes les clauses where, jointures de table, group-by et order-by. Ainsi, un index séparé sur chaque colonne peut ne pas toujours fonctionner, mais un index composite peut faire la magie.
AKHIL MATHEW
57

Imaginez que vous ayez les trois requêtes suivantes:

Requête I:

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

Requête II:

SELECT * FROM homes WHERE `geolat`=42.9

Requête III:

SELECT * FROM homes WHERE `geolng`=36.4

Si vous avez un index séparé par colonne, les trois requêtes utilisent des index. Dans MySQL, si vous avez un index composite ( geolat, geolng), seules les requêtes I et II (qui utilisent la première partie de l'index composite) utilisent des index. Dans ce cas, la requête III nécessite une recherche complète dans la table.

Dans la section Index à colonnes multiples du manuel, il est clairement expliqué comment fonctionnent les index à colonnes multiples, donc je ne veux pas retaper le manuel.

Depuis la page du manuel de référence MySQL :

Un index à plusieurs colonnes peut être considéré comme un tableau trié contenant des valeurs créées en concaténant les valeurs des colonnes indexées .

Si vous utilisez un index séparé pour les colonnes geolat et geolng, vous avez deux index différents dans votre table que vous pouvez rechercher indépendamment.

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 vous utilisez un index composite, vous n'avez qu'un seul index pour les deux colonnes:

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 est le numéro d'enregistrement relatif (pour simplifier, vous pouvez dire ID). Les deux premiers index générés séparément et le troisième index est composite. Comme vous pouvez le voir, vous pouvez effectuer une recherche basée sur geolng sur composite car il est indexé par geolat, mais il est possible de rechercher par geolat ou "geolat AND geolng" (puisque geolng est un index de deuxième niveau).

Jetez également un œil à la section du manuel Comment MySQL utilise les index .

Emre Yazici
la source
1
En fait, je n'ai aucune de ces questions. Ma requête est répertoriée dans le message d'origine. Ma requête est de renvoyer les maisons dans une grille carrée. Je connais le spatial et je n'essaye pas de calculer les distances. Je veux simplement savoir si l'utilisation d'un index composite a du sens lorsque j'essaie d'afficher toutes les maisons dans une géo-grille particulière (par exemple, quartier / ville / comté)
Teddy
Eyazici, j'ai mis à jour mon message d'origine (mise à jour 2). Ceci est ma requête réelle. Mon schéma de base de données actuel. Et ce que la commande EXPLAIN renvoie. Donc, avec cette information - devrais-je utiliser un index composite. Je ne suis toujours pas clair. Merci d'avance
Teddy
@ "En fait, je n'ai aucune de ces requêtes.". En fait, j'ai utilisé une simple condition WHERE pour expliquer la logique de base. Lors de l'utilisation d'un conditionnel (c.-à-d. WHERE) sur une colonne, MySQL essaie d'utiliser des index autant que possible. "x BETWEEN a AND b" est similaire à "x> a AND x <b". Vous avez utilisé à la fois des colonnes geolng et geolat dans votre requête conditionnelle. Si vous utilisez un index composite "(geolat, geolng)" votre "AND geolng BETWEEN ??? AND ???" conditionnel ne bénéficie pas des avantages de l'index (c'est pour MySQL). Donc , vous devez utiliser l' index séparé par colonne pour votre scénario.
Emre Yazici
Je ne comprends pas. Pourquoi devrais-je utiliser des index séparés pour la géolocalisation et la géolocalisation alors que j'effectuerai TOUJOURS une requête qui inclut les deux colonnes
Teddy
1
Non. Lorsqu'une "plage" est rencontrée (comme avec BETWEEN), aucun autre champ de l'index n'est pris en compte! L'indice composite n'est donc pas meilleur.
Rick James
19

Il pourrait y avoir une idée fausse sur ce que fait l'index composite. Beaucoup de gens pensent que l'index composite peut être utilisé pour optimiser une requête de recherche tant que la whereclause couvre les colonnes indexées, dans votre cas geolatet geolng. Allons plus loin:

Je crois que vos données sur les coordonnées des maisons seraient des décimales aléatoires en tant que telles:

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
...

Depuis geolatet les geolngvaleurs se répètent à peine. Un index composite sur geolatet geolngressemblerait à quelque chose comme ceci:

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
...

Par conséquent, la deuxième colonne de l'indice composite est fondamentalement inutile ! La vitesse de votre requête avec un index composite sera probablement similaire à celle d'un index sur la geolatcolonne uniquement .

Comme mentionné par Will, MySQL fournit un support d' extension spatiale . Un point spatial est stocké dans une seule colonne au lieu de deux lat lngcolonnes séparées . L'indice spatial peut être appliqué à une telle colonne. Cependant, l'efficacité pourrait être surfaite en fonction de mon expérience personnelle. Il se peut que l'index spatial ne résout pas le problème bidimensionnel mais accélère simplement la recherche en utilisant des R-Trees avec division quadratique .

Le compromis est qu'un point spatial consomme beaucoup plus de mémoire car il utilise des nombres à double précision de huit octets pour stocker les coordonnées. Corrigez-moi si je me trompe.

Dépassement de question
la source
5

Les index composites sont très puissants car ils:

  • Appliquer l'intégrité de la structure
  • Activer le tri sur un identifiant FILTRE

APPLIQUER L'INTÉGRITÉ DE LA STRUCTURE

Les index composites ne sont pas simplement un autre type d'index; ils peuvent fournir la structure NÉCESSAIRE à une table en imposant l'intégrité en tant que clé primaire.

Innodb de Mysql prend en charge le clustering et l'exemple suivant illustre pourquoi un index composite peut être nécessaire.

Pour créer un amis tables (pour un réseau social) nous avons besoin de 2 colonnes: user_id, friend_id.

Structure de la table

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

En vertu, une clé primaire (PK) est unique et en créant une PK composite, Innodb vérifiera automatiquement qu'aucun doublon user_id, friend_idn'existe lors de l'ajout d'un nouvel enregistrement. C'est le comportement attendu car aucun utilisateur ne doit avoir plus d'un enregistrement (lien de relation) avec friend_id = 2par exemple.

Sans PK composite, nous pouvons créer ce schéma à l'aide d'une clé de substitution:

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

Maintenant, chaque fois qu'un nouvel enregistrement est ajouté, nous devrons vérifier qu'un enregistrement précédent avec la combinaison user_id, friend_idn'existe pas déjà.

En tant que tel, un index composite peut renforcer l'intégrité de la structure.

ACTIVER LE TRI SUR UNE ID FILTRÉE

Il est très courant de trier un ensemble d'enregistrements par heure de publication (horodatage ou date / heure). Habituellement, cela signifie publier sur un identifiant donné. Voici un exemple

Table User_Wall_Posts (pensez aux messages sur le mur de Facebook)

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

Primary Key -> (user_id, timestamp, author_id)

Nous voulons interroger et trouver tous les articles pour user_id = 10et trier les articles de commentaire par timestamp(date).

Requête SQL

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

Le PK composite permet à Mysql de filtrer et de trier les résultats à l'aide de l'index; Mysql n'aura pas à utiliser un fichier temporaire ou un tri de fichiers pour récupérer les résultats. Sans une clé composite, cela ne serait pas possible et entraînerait une requête très inefficace.

En tant que telles, les clés composites sont très puissantes et conviennent plus que le simple problème de "Je veux rechercher column_a, column_bdonc j'utiliserai des clés composites. Pour mon schéma de base de données actuel, j'ai autant de clés composites que de clés simples. N'oubliez pas l'utilisation d'une clé composite!

ProfilTwist
la source
5

Les index composites sont utiles pour

  • 0 ou plusieurs clauses "=", plus
  • au plus une clause de plage.

Un index composite ne peut pas gérer deux plages. J'en discute plus en détail dans mon livre de recettes d'index .

Trouver le plus proche - Si la question concerne vraiment l' optimisation

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

alors aucun index ne peut vraiment gérer les deux dimensions.

Au lieu de cela, il faut «sortir des sentiers battus». Si une dimension est implémentée via le partitionnement et l'autre est implémentée en choisissant soigneusement le PRIMARY KEY, on peut obtenir une efficacité nettement meilleure pour les très grandes tables de recherche lat / lng. Mon dernier blog va dans les détails de la façon de mettre en œuvre "trouver le plus proche" sur le globe. Il comprend le code.

Ce PARTITIONssont des bandes de plages de latitude. Le PRIMARY KEYcommence délibérément par la longitude afin que les lignes utiles soient probablement dans le même bloc. Une routine stockée orchestre le code désordonné pour faire order by... limit...et pour faire croître le «carré» autour de la cible jusqu'à ce que vous ayez suffisamment de cafés (ou autre). Il prend également en charge les calculs du grand cercle et la gestion de la ligne de date et des pôles.

Plus

J'ai écrit un autre blog; il compare 5 façons de faire des recherches lat / lng: http://mysql.rjweb.org/doc.php/latlng#representation_choices (Il fait référence au lien donné ci-dessus comme l'un des 5.) Une des autres façons est la suivante, et il souligne qu'ils sont optimaux pour le cas particulier :

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

Autrement dit, il est important d'avoir les deux colonnes dans deux index et de ne pas avoir d'index à une seule colonne sur geolat et geolng.

Rick James
la source
1

Il n'y a pas de réponse unique en noir et blanc.

Vous devez utiliser un index composite, lorsque la charge de travail de votre requête en bénéficierait.

Vous devez profiler la charge de travail de votre requête pour le déterminer.

Un index composite entre en jeu lorsque les requêtes peuvent être entièrement satisfaites à partir de cet index.

MISE À JOUR (en réponse à la modification de la question posée): Si vous sélectionnez * dans le tableau, l'index composite peut être utilisé, ce n'est peut-être pas le cas. Vous devrez exécuter EXPLAIN PLAN pour être sûr.

Blé Mitch
la source
Est-il judicieux d'utiliser un index composite pour les données de géolocalisation (latitude et longitude)?
Teddy
1
Cela dépend entièrement des requêtes effectuées sur cette table.
Mitch Wheat
J'ai mis à jour mon message d'origine pour inclure la requête la plus courante effectuée. Voir au dessus.
Teddy
1

Pour effectuer des recherches spatiales, vous avez besoin d'un algorithme R-Tree , qui permet de rechercher très rapidement des zones géographiques. Exactement ce dont vous avez besoin pour ce travail.

Certaines bases de données ont des index spatiaux intégrés. Une recherche rapide sur Google montre que MySQL 5 en a (qui, en regardant votre SQL, je suppose que vous utilisez MySQL).

Volonté
la source
1

L'index composite peut être utile lorsque vous souhaitez optimiser la group byclause (consultez cet article http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html ). Votre attention s'il vous plaît:

Les conditions préalables les plus importantes pour l'utilisation d'index pour GROUP BY sont que toutes les colonnes GROUP BY référencent les attributs du même index et que l'index stocke ses clés dans l'ordre (par exemple, il s'agit d'un index BTREE et non d'un index HASH)

Alexandre
la source
GROUP BYn'a pas été mentionné.
Rick James
Il n'a pas été mentionné où? :) C'est évidemment mentionné dans l'article auquel j'ai fait référence. Et il répond aux questions qui ont été posées: Quand dois-je utiliser un index composite dans une base de données? Quelle est la ramification des performances en utilisant un index composite)? Pourquoi devrais-je utiliser un index composite?
Alexander
Correction: GROUP BYn'a pas été mentionné par l'OP.
Rick James
Bien sûr, c'était la réponse - l'un des cas où nous utiliserions un index composite dans une base de données.
Alexander
0

Je suis avec @Mitch, dépend entièrement de vos requêtes. Heureusement, vous pouvez créer et supprimer des index à tout moment, et vous pouvez ajouter le mot clé EXPLAIN à vos requêtes pour voir si l'analyseur de requêtes utilise les index.

Si vous recherchez une paire lat / longue exacte, cet indice aurait probablement du sens. Mais vous allez probablement chercher des maisons à une certaine distance d'un endroit particulier, donc vos requêtes ressembleront à ceci (voir source ):

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

et l'index ne sera probablement pas utile du tout. Pour les requêtes géospatiales, vous avez besoin de quelque chose comme ça .

Mise à jour: avec cette requête:

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

L'analyseur de requêtes peut utiliser un index sur geolat seul, ou un index sur geolng seul, ou éventuellement les deux index. Je ne pense pas qu'il utiliserait un index composite. Mais il est facile d'essayer chacune de ces permutations sur un ensemble de données réel, puis (a) voir ce que EXPLAIN vous dit et (b) mesurer le temps que prend réellement la requête.

Jim Ferrans
la source
J'utilise simplement le fait de vouloir retourner des maisons dans une grille carrée. Je connais le spatial, donc je n'essaye pas de calculer la distance. Je veux simplement retourner les maisons dans la grille carrée et je veux que cela fonctionne rapidement. En tant que tel, je veux m'assurer que mes index sont correctement configurés. Est ce que ça aide?
Teddy