2015-12-23 1 views
0

J'utilise un serveur MySQL dans lequel j'ai mis journal des requêtes lentes avec les paramètres suivantsMySQL journal lent, même après l'indexation des colonnes

log-slow-queries=/var/log/mysql/mysql-slow-queries.log 
long_query_time=1 
log-queries-not-using-indexes 

Je liste d'événements suivants pour chaque minute en cas de MySQL.

UPDATE mytable SET playing = 0 
    WHERE playing != 0 
     AND (TIMESTAMPDIFF(MINUTE , lastplayed, NOW()) >10) 
     OR (lastplayed IS NULL 
       AND ispresent= 0 
      ); 

Maintenant toutes ces colonnes playing, lastplayed, and ispresent sont indexés, mais encore ce apparaît dans le journal des requêtes lentes avec les détails suivants

# Query_time: 0.000585 Lock_time: 0.000159 Rows_sent: 0 Rows_examined: 316 

Pourquoi cette requête est affiché dans log lent?

+0

Vous supposez que la raison principale est que vous avez un OU, avec les clauses OR n'ayant pas tout à fait les mêmes colonnes. En outre, je soupçonne que vous avez un problème de priorité d'opérateur avec l'ET et l'OR (c'est-à-dire que _playing! = 0_ ne s'applique qu'au premier horodatage). – Kickstart

+0

Il a fallu moins d'une milliseconde, donc c'est très rapide. Pensez-vous qu'il y aura des millions de lignes dans la table un jour? Si oui, alors vous devriez vous en soucier. –

Répondre

0

Je suppose que lorsque vous utilisez lastplayed dans une fonction (dans ce cas TIMESTAMPDIFF), l'index ne peut pas être utilisé. MySQL n'est pas assez intelligent pour comprendre que vous recherchez all lastplayed stamps which are older than 10 minutes. Par conséquent, il va sur TOUTES les lignes.

Au lieu de cela, vous pouvez utiliser ceci:

... AND lastplayed < DATE_SUB(NOW(), INTERVAL 10 MINUTE) ... 

Ensuite, MySQL calcule la DATE_SUB qu'une seule fois et peut ensuite comparer lastplayed avec une valeur entière réelle. De cette façon, MySQL est encore assez intelligent pour utiliser des index.

Raison détaillée:
Imaginez que vous écrivez votre propre fonction MySQL (pseudo code):

function weirdFunction(Integer i): { 
    if (RAND() < 0.5) return i; 
    else return 0; 
} 

Maintenant, quand vous voulez exécuter votre requête comme:

... AND weirdFunction(lastplayed) = 0 ... 

comment est MySQL censé savoir quelles valeurs lastplayed donneront quel résultat? Ça ne peut pas. Il doit effectuer la fonction pour TOUTES les lignes afin de le découvrir. Et donc aucun index ne peut être utilisé.

0

Débarrassez-vous de la OR en faisant deux UPDATEs, un pour chaque côté du OR: (. C'est en supposant vous avez le droit de priorité)

UPDATE mytable SET playing = 0 
    WHERE lastplayed < NOW() - INTERVAL 10 MINUTE; 
UPDATE mytable SET playing = 0 
    WHERE lastplayed IS NULL 
     AND ispresent= 0; 

Ceux-ci ont besoin et utiliser celui-ci index "composite":

INDEX(lastplayed, ispresent) 

(Individuel dans les dexes sur chaque colonne ne seront pas aussi efficaces.)