2010-08-16 4 views
5

Je suis en train d'effectuer une mise à jour sur une table MySQL (moteur myisam) qui, selon le profileur, passe un temps démesuré dans l'état 'init':Requête MySQL: Longue durée passée dans l'état 'init'

mysql> show profile for query 2; 
+----------------------+-----------+ 
| Status    | Duration | 
+----------------------+-----------+ 
| starting    | 0.000057 | 
| checking permissions | 0.000006 | 
| Opening tables  | 0.000020 | 
| System lock   | 0.000007 | 
| Table lock   | 0.000005 | 
| init     | 21.911657 | 
| Updating    | 0.002363 | 
| end     | 0.000009 | 
| query end   | 0.000004 | 
| freeing items  | 0.000051 | 
| logging slow query | 0.000003 | 
| logging slow query | 0.000002 | 
| cleaning up   | 0.000005 | 
+----------------------+-----------+ 

la requête est la suivante:

mysql> update my_table 
    -> set rank = 
    -> greatest(
    ->  @rank := if(@score = score, @rank, @rank + 1), 
    ->  least(0, @score := score) 
    -> ) 
    -> where game=7 and zone=11 and ladder=2 
    -> order by score 
    -> limit 100; 

Query OK, 100 rows affected (21.92 sec) 
Rows matched: 100 Changed: 100 Warnings: 0 

J'ai un index composé sur toutes les colonnes figurant dans le « où » et « triez par » clauses (voir index « zone_lad_score » ci-dessous):

mysql> show indexes from my_table; 
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table    | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+ 
| my_table   |   1 | indx_e   |   1 | col_e  | A   |  2937401 |  NULL | NULL |  | BTREE  |   | 
| my_table   |   1 | zone_score  |   1 | zone   | A   |   217 |  NULL | NULL |  | BTREE  |   | 
| my_table   |   1 | zone_score  |   2 | score  | A   | 23499213 |  NULL | NULL | YES | BTREE  |   | 
| my_table   |   1 | zone_d_score |   1 | zone   | A   |   217 |  NULL | NULL |  | BTREE  |   | 
| my_table   |   1 | zone_d_score |   2 | col_d  | A   |  123355 |  NULL | NULL | YES | BTREE  |   | 
| my_table   |   1 | zone_d_score |   3 | score  | A   | 46998427 |  NULL | NULL | YES | BTREE  |   | 
| my_table   |   1 | zone_lad_score |   1 | zone   | A   |   217 |  NULL | NULL |  | BTREE  |   | 
| my_table   |   1 | zone_lad_score |   2 | ladder  | A   |   868 |  NULL | NULL | YES | BTREE  |   | 
| my_table   |   1 | zone_lad_score |   3 | score  | A   | 23499213 |  NULL | NULL | YES | BTREE  |   | 
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+ 

J'ai aussi la table partitionnée sur 'jeu', avec 10 partitions au total. Il y a environ 47 millions d'enregistrements totaux dans le tableau. Définition du tableau se présente comme suit:

my_table | CREATE TABLE `my_table` (
    `col_e` bigint(20) NOT NULL, 
    `zone` bigint(20) NOT NULL, 
    `score` int(11) DEFAULT NULL, 
    `game` tinyint(4) DEFAULT NULL, 
    `ladder` tinyint(4) DEFAULT NULL, 
    `col_d` int(11) DEFAULT NULL, 
    `rank` int(11) DEFAULT NULL, 
    KEY `indx_e` (`col_e`), 
    KEY `zone_score` (`zone`,`score`), 
    KEY `zone_d_score` (`zone`,`col_d`,`score`), 
    KEY `zone_lad_score` (`zone`,`ladder`,`score`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 
/*!50100 PARTITION BY LIST (game) 
(PARTITION p1 VALUES IN (1) ENGINE = MyISAM, 
PARTITION p2 VALUES IN (2) ENGINE = MyISAM, 
PARTITION p3 VALUES IN (3) ENGINE = MyISAM, 
PARTITION p4 VALUES IN (4) ENGINE = MyISAM, 
PARTITION p5 VALUES IN (5) ENGINE = MyISAM, 
PARTITION p6 VALUES IN (6) ENGINE = MyISAM, 
PARTITION p7 VALUES IN (7) ENGINE = MyISAM, 
PARTITION p8 VALUES IN (8) ENGINE = MyISAM, 
PARTITION p9 VALUES IN (9) ENGINE = MyISAM, 
PARTITION p10 VALUES IN (10) ENGINE = MyISAM) */ 

maintenant, selon la documentation MySQL (http://dev.mysql.com/doc/refman/5.0/en/general-thread-states.html), des actions dans l'état init comprennent « le rinçage du journal binaire, le journal InnoDB, et certaines opérations de nettoyage du cache de requêtes. » Ok ... alors puisque je n'utilise pas InnoDB, ça ne ressemble à rien qui devrait prendre beaucoup de temps.

Je suppose que je me demande pourquoi cette mise à jour, qui devrait utiliser un index, et affecter seulement 100 enregistrements prendrait si longtemps? Qu'est-ce qui le retarderait si longtemps dans l'état «init»? Si j'effectue une sélection sur les enregistrements de ciblage (sélectionnez * depuis my_table où game = 7 et zone = 11 et ladder = 2 order by limite de score 100), il revient presque instantanément. L'exécution de mises à jour similaires sur cette table (en utilisant l'index zone_d_score) prend moins d'une seconde. Qu'est-ce qui pourrait ralentir cette mise à jour?

Modifier: Ajout de la définition de la table, de la liste complète de tous les index de la table en question et des colonnes renommées pour faciliter le suivi.

Edit 2: Voici un « expliquer » sur la requête la plus proche de la mise à jour:

mysql> explain select * from my_table where game=7 and zone=11 and ladder=2 order by score limit 100; 
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+ 
| id | select_type | table    | type | possible_keys         | key    | key_len | ref   | rows | Extra  | 
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+ 
| 1 | SIMPLE  | my_table   | ref | zone_score,zone_d_score,zone_lad_score   | zone_lad_score | 10  | const,const | 53952 | Using where | 
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+ 
1 row in set (0.00 sec) 
+0

d'essayer de créer un index composé sur col_a, col_b, col_c. Normalement, mysql ne peut utiliser qu'un seul index par table dans une requête, donc vous n'aurez pas les avantages d'avoir trois index séparés. – nos

+0

L'index ci-dessus * est * un index composé, selon la colonne 'seq_in_index' de la commande 'show indexes'. Il a été créé via: create index my_index sur my_table (col_b, col_c, score); – odonnellt

+0

Pouvez-vous poster un EXPLAIN pour l'instruction SELECT correspondante pour voir quels index sont réellement utilisés par les clauses where/order? – Konerak

Répondre

1

Après un peu plus expérimenter, j'ai ajouté un index sur la table qui comprend également la colonne sur laquelle je partitionné la table:

CREATE INDEX game_zone_ladder_score ON my_table(game,zone,ladder,score) 

et soudainement le UPDATE fonctionne beaucoup mieux (sous-seconde). Je m'attendais à ce que UPDATE tire parti des partitions comme le font les SELECT, mais apparemment pas. J'aimerais quand même savoir ce que fait exactement MySQL pendant l'état 'init' pendant les UPDATEs, et/ou pourquoi les UPDATE n'honorent pas les partitions.

0

Si vous utilisez le partitionnement, les colonnes de partition doivent apparaître dans votre clé primaire. (Bullet point près du fond)

http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitions.html

+0

Vraie, mais seulement si votre table en fait a une clé primaire ou unique. Si ce n'est pas le cas, comme c'est le cas avec ma table, vous pouvez partitionner sur une autre colonne. – odonnellt

Questions connexes