Par exemple, j'ai une table homes
:
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 geolat
et geolng
, tel que:
Je remplace:
KEY `geolat` (`geolat`),
KEY `geolng` (`geolng`),
avec:
KEY `geolat_geolng` (`geolat`, `geolng`)
Si c'est le cas:
MISE À JOUR:
Étant donné que de nombreuses personnes ont déclaré que cela dépendait entièrement des requêtes que j'effectue, voici la requête la plus courante:
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? Pour le moment, je n'utilise PAS d'indice composite pour geolat et geolng. Devrais-je être?
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 une requête qui utilise ces champs pour la jointure, le filtrage et parfois la sélection. Les requêtes utilisant les sous-ensembles de colonnes les plus à gauche de ce composite bénéficieront également. Donc, l’index ci-dessus répondra aussi aux requêtes qui ont besoin
index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )
Mais cela ne le fera pas (du moins pas directement, peut-être que cela peut aider partiellement s'il n'y a pas de meilleurs indices) pour les requêtes nécessitant
index( column_A, column_C )
Notez 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 par elle-même, mais pas la dimension la plus à droite par elle-même. Si vous interrogez toujours deux dimensions, un index composite est la solution, peu importe la première (probablement).
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 la requête I et la requête II (qui utilise la première partie de l'index composite) utilisent des index. Dans ce cas, la requête III requiert une recherche de table complète.
Dans la section Index de plusieurs colonnes du manuel, le fonctionnement de plusieurs index de colonne est clairement expliqué. Par conséquent, je ne souhaite pas retaper manuellement.
De la page du manuel de référence de 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 des index séparés pour les colonnes geolat et geolng, vous avez deux index différents dans votre table dans lesquels vous pouvez effectuer une recherche indépendante.
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 ne disposez que d'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 sont séparés et le troisième est composite. Comme vous pouvez le constater, vous pouvez effectuer une recherche basée sur geolng sur composite car elle est indexée par geolat. Il est toutefois possible d'effectuer une recherche par geolat ou "geolat AND geolng" (puisque geolng est un index de second niveau).
Regardez aussi Comment MySQL utilise les index section du manuel.
Il pourrait y avoir une idée fausse sur ce que fait l'indice composite. Beaucoup de gens pensent que l'index composite peut être utilisé pour optimiser une requête de recherche tant que la clause where
couvre les colonnes indexées, dans votre cas geolat
et geolng
. Approfondissons:
Je crois que vos données sur les coordonnées des maisons seraient des nombres décimaux aléatoires en tant que tels:
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 geolat
et geolng
les valeurs se répètent à peine. Un index composite sur geolat
et geolng
ressemblerait à 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'index composite est fondamentalement inutile ! La vitesse de votre requête avec un index composite sera probablement similaire à celle d’un index de la colonne geolat
.
Comme mentionné par Will, MySQL fournit le support extension spatiale . Un point spatial est stocké dans une seule colonne au lieu de deux colonnes lat
lng
distinctes. L'index spatial peut être appliqué à une telle colonne. Cependant, l'efficacité pourrait être surestimée en fonction de mon expérience personnelle. Il se peut que l’index spatial ne résolve pas le problème à deux dimensions mais accélère simplement la recherche en utilisant R-Trees avec division quadratique.
Le compromis est qu'un point spatial consomme beaucoup plus de mémoire car il utilisait des nombres à double précision de huit octets pour stocker les coordonnées. Corrigez-moi si je me trompe.
Les indices composites sont utiles pour
Un index composite ne peut pas gérer deux plages. J'en discute plus avant dans mon index cookbook .
Trouver le plus proche - Si la question est vraiment sur l'optimisation
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
then no index 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 un partitionnement et que l’autre l’est en sélectionnant soigneusement le PRIMARY KEY
, on peut obtenir une efficacité nettement meilleure pour les très grandes tables de recherche lat/long. Mon blogueur ) explique en détail comment implémenter "trouver le plus proche" sur le globe. Il comprend du code.
Les PARTITIONs
sont des bandes de plages de latitude. Le PRIMARY KEY
commence délibérément par la longitude, de sorte que les lignes utiles se trouvent probablement dans le même bloc. Une routine stockée orchestre le code en désordre pour faire order by... limit...
et pour agrandir 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 manipulation de la ligne de dates et des pôles.
Les index composites sont très puissants car ils:
APPLIQUER L'INTÉGRITÉ DE LA STRUCTURE
Les index composites ne sont pas simplement un autre type d'index; ils peuvent fournir la structure NECESSARY à une table en appliquant l'intégrité en tant que clé primaire.
Mysql Innodb prend en charge la mise en cluster et l'exemple suivant montre pourquoi un index composite peut être nécessaire.
Pour créer une table d'amis (c'est-à-dire pour un réseau social), nous avons besoin de 2 colonnes: user_id, friend_id
.
Strcture de table
user_id (medium_int)
friend_id (medium_int)
Primary Key -> (user_id, friend_id)
En vertu de la clé primaire, une clé primaire est unique. En créant une clé composite, Innodb vérifie automatiquement qu’il n’ya pas de doublons sur user_id, friend_id
existe lorsqu'un nouvel enregistrement est ajouté. Ceci est le comportement attendu car aucun utilisateur ne devrait avoir plus d'un enregistrement (lien de relation) avec friend_id = 2
par exemple.
Sans PK composite, nous pouvons créer ce schéma en utilisant 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_id
n'existe pas déjà.
En tant que tel, un index composite peut imposer 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 en fonction de l'heure de la publication (horodatage ou date/heure). Habituellement, cela signifie publier sur un identifiant donné. Voici un exemple
Table User_Wall_Posts (pensez si les publications 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 = 10
et triez les commentaires de commentaires par timestamp
(date).
QUERY SQL
SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES
La 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 portage de fichiers pour récupérer les résultats. Sans une clé composite, cela ne serait pas possible et causerait une requête très inefficace.
En tant que telles, les touches composites sont très puissantes et conviennent plus au simple problème de "Je veux rechercher column_a, column_b
donc je vais utiliser 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. Ne négligez pas l'utilisation d'une clé composite!
L'index composite peut être utile lorsque vous souhaitez optimiser group by
clause (consultez cet article http://dev.mysql.com/doc/refman/5.0/fr/group-by-optimization.html ). Votre attention s'il vous plaît:
Les conditions préalables les plus importantes pour l'utilisation des index pour GROUP BY sont que toutes les colonnes GROUP BY référencent des 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).
Pour faire 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 dans Google indique que MySQL 5 les possède (ce qui, vu votre code SQL, suppose que vous utilisez MySQL).
Il n'y a pas de noir et blanc, une réponse unique.
Vous devez utiliser un index composite, lorsque votre charge de travail de requête en bénéficierait.
Vous devez déterminer votre charge de travail de la requête afin de déterminer votre charge de travail.
Un index composite entre en jeu lorsque les requêtes peuvent être entièrement satisfaites à partir de cet index.
UPDATE (en réponse à la question de modification en publication): Si vous sélectionnez * dans la table, l'index composite peut être utilisé, mais ce n'est pas le cas. Vous devrez exécuter EXPLAIN PLAN pour en être sûr.
Je suis avec @Mitch, tout dépend de vos questions. 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ête utilise les index.
Si vous recherchez une paire exacte lat/long, cet indice aura probablement un sens. Mais vous allez probablement chercher des maisons à une certaine distance d'un lieu particulier. 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 très probablement pas utile du tout. Pour les requêtes géospatiales, vous avez besoin de quelque chose comme this .
Mise à jour: avec cette requête:
SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
L'analyseur de requête 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) de voir ce que EXPLAIN vous dit et (b) de mesurer le temps que prend réellement la requête.