2010-08-16 176 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不支持分區。