Mysql
 sql >> Base de données >  >> RDS >> Mysql

Requête MySQL MyISAM slow count() malgré la couverture de l'index

Voici ce qui se passe.

The SELECT COUNT (...) icd_index where icd='25000'

utilisera l'index, qui est un BTree séparé des données. Mais il le scanne de cette manière :

  1. Recherchez la première entrée ayant icd='25000'. C'est presque instantané.
  2. Balayer vers l'avant jusqu'à ce qu'il trouve un changement dans icd. Cela numérisera uniquement l'index, sans toucher aux données. Selon EXPLAIN, il y aura environ 910 104 entrées d'index à parcourir.

Regardons maintenant le BTree pour cet index. Sur la base des champs de l'index, chaque ligne fera exactement 22 octets, plus il y aura une surcharge (estimation 40%). Un bloc d'index MyISAM fait 1Ko (cf les 16Ko d'InnoDB). J'estimerais 33 lignes par bloc. 910,104/33 dit qu'environ 27K blocs doivent être lus pour faire le COUNT. (Remarque COUNT(core_id) doit vérifier core_id pour être nul, COUNT(*) ne fait pas; c'est une différence mineure.) La lecture de blocs de 27K sur un disque dur ordinaire prend environ 270 secondes. Vous avez eu de la chance de le faire en 60 secondes.

La deuxième exécution a trouvé tous ces blocs dans le key_buffer (en supposant que key_buffer_size est d'au moins 27 Mo), il n'a donc pas eu à attendre le disque. C'était donc beaucoup plus rapide. (Ceci en ignorant le cache de la requête, que vous avez eu la sagesse de vider ou d'utiliser SQL_NO_CACHE.)

5.6 se trouve être hors de propos (mais merci de l'avoir mentionné), puisque ce processus n'a pas changé depuis 4.0 ou avant (sauf que utf8 n'existait pas ; plus de détails ci-dessous).

Passer à InnoDB aiderait de plusieurs manières. La CLÉ PRIMAIRE serait "groupée" avec les données, et non stockée dans un BTree séparé. Ainsi, une fois la donnée ou la PK mise en cache, l'autre est immédiatement disponible. Le nombre de blocs serait plus proche de 5K, mais ce serait des blocs de 16Ko. Ceux-ci peuvent être chargés plus rapidement si le cache est froid.

Vous demandez "Ai-je besoin d'un index sur icd seul ?" situation de cache froid).

Une autre pensée est, si icd est toujours numérique et toujours numérique, pour utiliser MEDIUMINT UNSIGNED , et virez sur ZEROFILL s'il peut avoir des zéros non significatifs.

Oups, je n'ai pas remarqué le CHARACTER SET. (J'ai corrigé les chiffres ci-dessus, mais laissez-moi élaborer.)

  • CHAR(5) autorise 5 caractères .
  • ascii prend 1 octet par caractère .
  • utf8 prend jusqu'à 3 octets par caractères .
  • Donc, CHAR(5) CHARACTER SET utf8 prend 15 octets toujours .

Changer la colonne en CHAR(5) CHARACTER SET ascii le réduirait à 5 octets.

Le changer en MEDIUMINT UNSIGNED ZEROFILL le réduirait à 3 octets.

Réduire les données accélérerait les E/S d'une quantité à peu près proportionnelle (après avoir autorisé 6 octets supplémentaires pour les deux autres champs.