2016-12-08 1 views
3

Cette requête est très lente. C'est assez simple et les 3 tables utilisées sont indexées sur toutes les colonnes dans les clauses JOIN et WHERE. Comment puis-je optimiser ma requête ou mes tables pour cette requête?Comment optimiser cette requête MySql - rejoint 3 tables?

Ceci est la requête lente. Cela prend 15-20 secondes pour courir.

SELECT 
    user.id, 
    user.name, 
    user.key, 
    user.secret, 
    account.id, 
    account.name, 
    account.admin, 
    setting.attribute, 
    setting.value 
    FROM  user 
    INNER JOIN account ON account.id  = user.account_id 
    INNER JOIN setting ON setting.user_id = user.id 
    AND setting.deleted = 0 
    WHERE user.deleted = 0 

Il est question probable est due à se joindre à la table de réglage, car le deux requêtes ci-dessous prennent environ 5 secondes au total. Bien que, 5 secondes semble encore un peu long?

SELECT 
    user.id, 
    user.name, 
    user.user_key, 
    user.secret, 
    account.id, 
    account.name, 
    account.admin 
    FROM  user 
    INNER JOIN account ON account.user_id = user.id 
    WHERE user.deleted = 0 

    SELECT 
    setting.user_id, 
    setting.attribute, 
    setting.value 
    FROM setting 
    WHERE setting.deleted = 0 

Explain pour la requête lente:

id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra 

1, 'SIMPLE', 'user', 'ALL', 'PRIMARY,idx_id,idx_deleted', null, null, null, 600, 'Using where' 
1, 'SIMPLE', 'account', 'eq_ref', 'PRIMARY', 'PRIMARY', '8', 'user.account_id', 1, null 
1, 'SIMPLE', 'setting', 'ref', 'attribute_version_unique,idx_user_id,indx_deleted', 'attribute_version_unique', '8', 'user.id', 35, 'Using where' 

Le schéma:

CREATE TABLE user 
(
    id BIGINT(20) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    name VARCHAR(45) NOT NULL, 
    user_key VARCHAR(45) NOT NULL, 
    secret VARCHAR(16), 
    account_id BIGINT(20) unsigned NOT NULL, 
    name VARCHAR(40) NOT NULL, 
    demo TINYINT(1) DEFAULT '0' NOT NULL, 
    details VARCHAR(4000), 
    date_created DATETIME NOT NULL, 
    date_modified DATETIME NOT NULL, 
    deleted TINYINT(1) DEFAULT '0' NOT NULL 
); 
CREATE INDEX idx_date_modified ON user (date_modified); 
CREATE INDEX idx_deleted ON user (deleted); 
CREATE INDEX idx_id ON pub_application (id); 
CREATE UNIQUE INDEX idx_name_unique ON user (user_key); 
CREATE TABLE account 
(
    id BIGINT(20) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    name VARCHAR(100) NOT NULL, 
    display_name VARCHAR(100), 
    admin TINYINT(1) DEFAULT '0' NOT NULL, 
    visibility VARCHAR(15) DEFAULT 'public', 
    cost DOUBLE, 
    monthly_fee VARCHAR(300), 
    date_created DATETIME NOT NULL, 
    date_modified DATETIME NOT NULL, 
    deleted TINYINT(1) DEFAULT '0' 
); 
CREATE INDEX idx_date_modified ON account (date_modified); 
CREATE TABLE setting 
(
    id BIGINT(20) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    user_id BIGINT(20) unsigned NOT NULL, 
    attribute VARCHAR(45) NOT NULL, 
    value VARCHAR(4000), 
    date_created DATETIME NOT NULL, 
    date_modified DATETIME NOT NULL, 
    deleted TINYINT(1) DEFAULT '0' NOT NULL 
); 
CREATE UNIQUE INDEX attribute_version_unique ON setting (user_id, attribute); 
CREATE INDEX idx_user_id ON setting (user_id); 
CREATE INDEX idx_date_modified ON setting (date_modified); 
CREATE INDEX indx_deleted ON setting (deleted); 
+0

Voilà une question bien formulée sur les performances de requête. Marche à suivre! –

+0

Vous avez seulement 600 lignes dans 'users' et toutes les jointures ont utilisé des index de sorte que la requête doit s'exécuter rapidement. Peut-être votre problème dans le serveur? –

+0

Je ne suis pas vraiment sûr mais je ne trouve pas d'index pour 'account_id' dans la table' users' et comme je comprends sa clé étrangère pour la relation avec la table 'account'. En rejoignant 'ON account.id = user.account_id', il ralentit probablement toute la requête. –

Répondre

3

Avec respect, vous avez trébuché sur un antipattern commun. L'indexation "toutes les colonnes" est généralement un inutile déplacer. MySQL (fin 2016) peut exploiter au plus un index par table lors de la satisfaction d'une requête. Donc, les index supplémentaires sont susceptibles de ne faire aucune requête, et certainement ajouter des frais généraux sur les opérations INSERT et UPDATE.

Cette requête pourrait être améliorée par certains index de recouvrement composites. Essayez cet index sur votre table user. C'est un couvrant l'index: destiné à contenir toutes les colonnes nécessaires pour satisfaire la requête. Il est organisé dans un ordre correspondant à votre clause WHERE.

CREATE INDEX idx_user_account_setting 
      ON user (deleted , account_id, id, name, key, secret); 

Cet indice de couverture pourrait aider sur votre table setting

CREATE INDEX idx_setting_user 
      ON setting (user_id, deleted , attribute, value); 

Essayez aussi celui-ci, l'ordre de commutation des deux premières colonnes, si le premier ne permet pas.

CREATE INDEX idx_setting_user_alt 
      ON setting (deleted, user_id, attribute, value); 

Enfin, essayez celui-ci sur account. S'il vous plaît, si ces suggestions aident à laisser un bref commentaire indiquant combien ils ont aidé.

Lisez ceci. http://use-the-index-luke.com/

+0

Merci pour les conseils détaillés, je vais essayer et faire un rapport. Dois-je supprimer les autres indices pour m'assurer que les indices de couverture sont utilisés? – ab11

+0

Les indices couverts n'ont pas fait beaucoup de différence. J'ai trouvé que tout sélectionner à partir de la table de paramètres sans clause where prend ~ 5 secondes, il a ~ 40k lignes. 5 secondes semble assez longue? Et si cette requête prend 5 secondes, il n'est pas choquant que la jointure prenne 15? – ab11