Pourquoi est-il nécessaire ?
Lorsque les données sont stockées sur des dispositifs de stockage sur disque, elles sont stockées sous forme de blocs de données. Ces blocs sont accessibles dans leur intégralité, ce qui en fait l'opération atomique d'accès au disque. Les blocs de disque sont structurés de la même manière que les listes chaînées ; tous deux contiennent une section pour les données, un pointeur vers l'emplacement du nœud (ou bloc) suivant, et tous deux ne doivent pas être stockés de manière contiguë.
Étant donné qu'un certain nombre d'enregistrements ne peuvent être triés que sur un seul champ, nous pouvons affirmer que la recherche sur un champ qui n'est pas trié nécessite une recherche linéaire qui requiert (N+1)/2
accès aux blocs (en moyenne), où N
est le nombre de blocs que la table couvre. Si ce champ n'est pas un champ clé (c'est-à-dire qu'il ne contient pas d'entrées uniques), il faut rechercher dans tout le tablespace à N
les accès aux blocs.
Alors qu'avec un champ trié, une recherche binaire peut être utilisée, qui a log2 N
les accès aux blocs. De plus, comme les données sont triées en fonction d'un champ non clé, il n'est pas nécessaire de rechercher les valeurs en double dans le reste de la table, une fois qu'une valeur supérieure a été trouvée. L'augmentation des performances est donc substantielle.
Qu'est-ce que l'indexation ?
L'indexation est un moyen de trier un certain nombre d'enregistrements sur plusieurs champs. La création d'un index sur un champ dans une table crée une autre structure de données qui contient la valeur du champ et un pointeur vers l'enregistrement auquel il se rapporte. Cette structure d'index est ensuite triée, ce qui permet d'y effectuer des recherches binaires.
L'inconvénient de l'indexation est que ces index nécessitent de l'espace supplémentaire sur le disque puisque les index sont stockés ensemble dans une table utilisant le moteur MyISAM, ce fichier peut rapidement atteindre les limites de taille du système de fichiers sous-jacent si de nombreux champs de la même table sont indexés.
Comment cela fonctionne-t-il ?
Tout d'abord, présentons un exemple de schéma de table de base de données ;
Field name Data type Size on disk
id (Primary key) Unsigned INT 4 bytes
firstName Char(50) 50 bytes
lastName Char(50) 50 bytes
emailAddress Char(100) 100 bytes
Note Le format char a été utilisé à la place du format varchar afin d'obtenir une taille précise sur le disque. Cette base de données échantillon contient cinq millions de lignes et n'est pas indexée. Les performances de plusieurs requêtes vont maintenant être analysées. Il s'agit d'une requête utilisant la méthode id (un champ clé trié) et un autre utilisant le champ premierNom (un champ non trié sans clé).
Exemple 1 - champs triés et non triés
Compte tenu de notre base de données d'échantillons de r = 5,000,000
enregistrements d'une taille fixe, ce qui donne une longueur d'enregistrement de R = 204
octets et ils sont stockés dans une table en utilisant le moteur MyISAM qui utilise la taille de bloc par défaut. B = 1,024
octets. Le facteur de blocage de la table serait bfr = (B/R) = 1024/204 = 5
enregistrements par bloc de disque. Le nombre total de blocs nécessaires pour contenir la table est de N = (r/bfr) = 5000000/5 = 1,000,000
blocs.
Une recherche linéaire sur le champ id nécessiterait en moyenne N/2 = 500,000
accède au bloc pour trouver une valeur, étant donné que le champ id est un champ clé. Mais comme le champ id est également trié, on peut effectuer une recherche binaire nécessitant en moyenne log2 1000000 = 19.93 = 20
bloquer les accès. Nous pouvons immédiatement constater qu'il s'agit d'une amélioration radicale.
Maintenant, le premierNom n'est ni trié ni un champ clé, de sorte qu'une recherche binaire est impossible. Les valeurs ne sont pas non plus uniques, et la table devra donc être recherchée jusqu'à la fin pour obtenir une valeur exacte. N = 1,000,000
bloquer les accès. C'est cette situation que l'indexation vise à corriger.
Étant donné qu'un enregistrement d'index ne contient que le champ indexé et un pointeur vers l'enregistrement original, il va de soi qu'il sera plus petit que l'enregistrement multi-champs vers lequel il pointe. L'index lui-même nécessite donc moins de blocs de disque que la table d'origine, qui nécessite donc moins d'accès aux blocs pour être parcourue. Le schéma d'un index sur la table premierNom est présenté ci-dessous ;
Field name Data type Size on disk
firstName Char(50) 50 bytes
(record pointer) Special 4 bytes
Note : Les pointeurs dans MySQL ont une longueur de 2, 3, 4 ou 5 octets en fonction de la taille de la table.
Exemple 2 - indexation
Compte tenu de notre base de données d'échantillons de r = 5,000,000
enregistrements avec une longueur d'enregistrement d'index de R = 54
octets et en utilisant la taille de bloc par défaut B = 1,024
octets. Le facteur de blocage de l'index serait bfr = (B/R) = 1024/54 = 18
enregistrements par bloc de disque. Le nombre total de blocs nécessaires pour contenir l'index est de N = (r/bfr) = 5000000/18 = 277,778
blocs.
Maintenant, une recherche utilisant le premierNom peuvent utiliser l'index pour améliorer les performances. Ceci permet une recherche binaire de l'index avec une moyenne de log2 277778 = 18.08 = 19
accès aux blocs. Pour trouver l'adresse de l'enregistrement réel, qui nécessite un autre accès au bloc pour être lu, ce qui porte le total à 19 + 1 = 20
accès aux blocs, ce qui est bien loin des 1 000 000 d'accès aux blocs nécessaires pour trouver une premierNom dans la table non indexée.
Quand doit-on l'utiliser ?
Étant donné que la création d'un index nécessite de l'espace disque supplémentaire (277 778 blocs de plus dans l'exemple ci-dessus, soit une augmentation de ~28%), et qu'un trop grand nombre d'index peut entraîner des problèmes liés aux limites de taille des systèmes de fichiers, il faut bien réfléchir pour sélectionner les bons champs à indexer.
Étant donné que les index ne sont utilisés que pour accélérer la recherche d'un champ correspondant dans les enregistrements, il va de soi que l'indexation des champs utilisés uniquement pour la sortie serait un simple gaspillage d'espace disque et de temps de traitement lors d'une opération d'insertion ou de suppression, et devrait donc être évitée. De plus, étant donné la nature d'une recherche binaire, la cardinalité ou l'unicité des données est importante. L'indexation d'un champ dont la cardinalité est de 2 divise les données en deux, alors qu'une cardinalité de 1 000 renvoie environ 1 000 enregistrements. Avec une cardinalité aussi faible, l'efficacité est réduite à un tri linéaire, et l'optimiseur de requêtes évitera d'utiliser l'index si la cardinalité est inférieure à 30 % du nombre d'enregistrements, ce qui fait de l'index un gaspillage d'espace.