2010-08-16 154 views
5

我正在执行一个MySQL表(myisam引擎)的更新,根据分析器,它在'初始化'状态花费过多时间:MySQL查询:在'初始化'状态花费的时间太长

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 | 
+----------------------+-----------+ 

查询如下:

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 

我对在“其中”和列出的所有列的复合索引“以便通过”条款(见下文命名为“zone_lad_score”指数):

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  |   | 
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+ 

我也有'游戏'分区,共有10个分区。表中总共有约4700万条记录。表定义如下:

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) */ 

现在,根据MySQL的文档(http://dev.mysql.com/doc/refman/5.0/en/general-thread-states.html),在“初始化”状态的行动包括“刷新二进制日志,InnoDB的日志,以及一些查询缓存清理操作。”好吧...因为我没有使用InnoDB,听起来不像是需要花费很多时间的东西。

我想我想知道为什么这个更新应该使用索引,只影响100个记录会花费这么长时间?特别是在'初始'状态持续这么久会有什么特别的呢?如果我在定位记录上执行选择(select * from my_table where game = 7 and zone = 11 and ladder = 2 order by score limit 100),它几乎立即返回。在该表上执行类似的更新(使用zone_d_score索引)不到一秒钟。什么可能会放慢这个特定的更新?

编辑:增加了表格定义,问题表中所有索引的完整列表,以及重命名的列使事情更容易遵循。

编辑2:这里有最接近更新查询的“解释”:

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

尝试创建上为col_a,col_b,col_c一个复合索引。通常,mysql只能在查询中为每个表使用1个索引,所以你不能从3个独立索引中获得全部好处。 – nos 2010-08-16 08:14:33

+0

上面显示的索引*是一个复合索引,按照'show indexes'命令中的'seq_in_index'列。它是通过以下方式创建的:在my_table上创建索引my_index(col_b,col_c,score); – odonnellt 2010-08-16 08:17:05

+0

您能否为相应的SELECT语句发布EXPLAIN以查看哪些索引实际上由where/order子句使用? – Konerak 2010-08-16 08:54:05

回答

1

经过一些尝试,我加在表上的索引,其中还包括关于这一点我分区列该表:

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

突然UPDATE表现好得多(亚秒)。我希望UPDATE能像SELECT那样利用分区,但显然不是。

在UPDATE期间,仍然想知道MySQL在'init'状态期间究竟做了什么,和/或为什么UPDATE不支持分区。