web-dev-qa-db-fra.com

requête SQL simple sur wp_postmeta très lente

J'ai une installation wordpress et j'ai besoin de cette requête pour chaque message:

select post_id, meta_key from wp_postmeta where meta_key = 'mykey' and meta_value = 'somevalue'

J'ai 3M lignes sur cette table, et la requête prend environ 6 secondes pour terminer. Je pense que cela devrait être beaucoup plus rapide. Si je montre l'index de la table, il me renvoie:

SHOW INDEX FROM wp_postmeta

Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type  Comment Index_comment   
wp_postmeta 0   PRIMARY 1   meta_id A   3437260 NULL    NULL        BTREE       
wp_postmeta 1   post_id 1   post_id A   1718630 NULL    NULL        BTREE       
wp_postmeta 1   meta_key    1   meta_key    A   29  191 NULL    YES BTREE   

Si je fais une explication, cela me retourne ceci:

explain select post_id, meta_key from wp_postmeta where meta_key = 'mykey' and meta_value = 'somevalue'


id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  wp_postmeta ref meta_key    meta_key    767 const   597392  Using where

Je ne suis pas très bon dans mysql, donc je ne sais pas comment le vérifier ou le résoudre. Pouvez-vous me donner une idée du problème?

Merci à tous.

4
superisc

wp_postmeta a des index inefficaces. Le tableau publié (voir Wikipedia) est

CREATE TABLE wp_postmeta (
  meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  post_id bigint(20) unsigned NOT NULL DEFAULT '0',
  meta_key varchar(255) DEFAULT NULL,
  meta_value longtext,
  PRIMARY KEY (meta_id),
  KEY post_id (post_id),
  KEY meta_key (meta_key)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Les problèmes:

  • Le AUTO_INCREMENT n'apporte aucun avantage; en fait, cela ralentit la plupart des requêtes (à cause du fait qu’il faut chercher dans l’index secondaire pour trouver auto_inc id, puis chercher dans les données l’identifiant réel dont vous avez besoin)
  • Le AUTO_INCREMENT est un fouillis supplémentaire - à la fois sur le disque et dans le cache.
  • Mieux vaut PRIMARY KEY(post_id, meta_key) - en cluster, gère les deux parties de JOIN habituelles.
  • BIGINT est excessif, mais cela ne peut pas être corrigé sans changer d'autres tables.
  • VARCHAR(255) peut être un problème dans MySQL 5.6 avec utf8mb4; voir les solutions de contournement ci-dessous.
  • Quand meta_key ou meta_value seront-ils jamais NULL?

Les solutions:

CREATE TABLE wp_postmeta (
    post_id BIGINT UNSIGNED NOT NULL,
    meta_key VARCHAR(255) NOT NULL,
    meta_value LONGTEXT NOT NULL,
    PRIMARY KEY(post_id, meta_key),
    INDEX(meta_key)
    ) ENGINE=InnoDB;

L'utilisation typique:

JOIN wp_postmeta AS m  ON p.id = m.post_id
WHERE m.meta_key = '...'

Remarques:

  • Le PRIMARY KEY composite va directement à la ligne souhaitée, sans digression via l'index secondaire ni avec la recherche sur plusieurs lignes.
  • INDEX(meta_key) peut être utile ou non, en fonction des autres requêtes que vous avez.
  • InnoDB est requis pour le 'clustering'.
  • À l'avenir, utilisez utf8mb4, pas utf8. Cependant, vous devez être cohérent dans toutes les tables WP et dans vos paramètres de connexion.

L'erreur "longueur maximale de la clé est 767", ce qui peut arriver dans MySQL 5.6 lorsque vous essayez d'utiliser CHARACTER SET utf8mb4. Effectuez l'une des opérations suivantes (chacune présente un inconvénient) pour éviter l'erreur:

  • Effectuez une mise à niveau vers la version 5.7.7 pour une limite de 3 072 octets - votre nuage peut ne pas fournir cette information;
  • Changez 255 en 191 sur le VARCHAR - vous perdez les clés de plus de 191 caractères (improbable?);
  • ALTER .. CONVERT TO UT8 - vous perdez Emoji et des Chinois;
  • Utilisez un index "préfixe" - vous perdez certains des avantages en termes de performances;
  • Reconfigurer (si vous restez avec 5.6.3 - 5.7.6) - 4 choses à changer: Barracuda + innodb_file_per_table + innodb_large_prefix + dynamique ou compressé.

Incompatibilités potentielles

  • meta_id n'est probablement pas utilisé nulle part. (Mais c'est risqué de l'enlever).
  • Vous pouvez conserver meta_id et tirer le meilleur parti des avantages de l’utilisation des index suivants: PRIMARY KEY(post_id, meta_key, meta_id), INDEX(meta_id), INDEX(meta_key, post_id). (Remarque: en ayant meta_id à la fin de la PK, il est possible que post_id + meta_key soit non unique.)
  • Passer de BIGINT à un type de données plus petit impliquerait également de changer d'autres tables.

utf8mb4

  • Passer à 5.7 ne devrait pas être incompatible.
  • Réduire à VARCHAR(191) nécessiterait que l'utilisateur comprenne que la limite est maintenant le "191" arbitraire au lieu de la limite arbitraire précédente de "255".
  • Le correctif de reconfiguration concerne les problèmes d'administrateur de base de données, et non d'incompatibilité.

Commentaire

J'espère que certaines de mes recommandations sont sur la feuille de route WordPress. Pendant ce temps, stackoverflow et dba.stackexchange sont encombrés par "pourquoi WP est-il si lent". Je pense que les corrections apportées ici réduiraient considérablement le nombre de questions de ce type.

Notez que certains utilisateurs passent à utf8mb4 malgré des problèmes de compatibilité. Ensuite, ils ont des ennuis. J'ai essayé de résoudre tous les problèmes rencontrés MySQL.

Tiré du blog de Rick James mysql: source

1
Rick James

Si vous recherchez des performances de base de données "prêtes à l'emploi", la méta-table de publication est le mauvais endroit pour stocker les éléments que vous souhaitez rechercher. Dans la façon dont vous décrivez votre requête, il sera bien préférable d’utiliser une taxonomie pour ce cas d’utilisation.

Mais si vous êtes déjà trop "au fond du trou" pour restructurer votre base de données, vous devriez vous pencher sur la solution de mise en cache, qui pourrait ne pas aider à accélérer la requête, mais avec une bonne mise en cache, vous ne subirez le retard qu'une seule fois. Utilisez cron pour générer le cache si nécessaire, et l'impact sur l'utilisateur sera nul.

1
Mark Kaplun