MySQL 查询:长时间处于“init”状态
Posted
技术标签:
【中文标题】MySQL 查询:长时间处于“init”状态【英文标题】:MySQL Query: Long time spent in 'init' state 【发布时间】:2010-08-16 08:11:01 【问题描述】:我正在对 mysql 表(myisam 引擎)执行更新,根据分析器的说法,该表在“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 |
+----------------------+-----------+
查询如下:
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
我对“where”和“order by”子句中列出的所有列都有一个复合索引(请参阅下面名为“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),“init”状态下的操作包括“刷新二进制日志、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)
【问题讨论】:
尝试在 col_a,col_b,col_c 上创建复合索引。通常 mysql 在查询中每个表只能使用 1 个索引,因此您不会从拥有 3 个单独的索引中获得全部好处。 上面显示的索引是一个复合索引,根据“显示索引”命令中的“seq_in_index”列。它是通过以下方式创建的:create index my_index on my_table(col_b, col_c, score); 您能否为相应的 SELECT 语句发布一个 EXPLAIN 以查看 where/order 子句实际使用了哪些索引? 您的数据库中有多少行?在〜50000之后它会因为太多的逻辑而减速,还包括ORDER BY
...我会说init正在解析greater
和least
函数...
【参考方案1】:
经过更多试验,我在表上添加了一个索引,该索引还包括我对表进行分区的列:
CREATE INDEX game_zone_ladder_score ON my_table(game,zone,ladder,score)
突然间,UPDATE 的表现要好得多(亚秒级)。我本来希望 UPDATE 像 SELECT 一样利用分区,但显然不是。
仍然很想知道 MySQL 在 UPDATE 期间的“init”状态期间到底在做什么,和/或为什么 UPDATE 不支持分区。
【讨论】:
【参考方案2】:如果您使用分区,则要求分区列必须出现在您的主键中。 (靠近底部的子弹点)
http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitions.html
【讨论】:
正确,但前提是您的表实际上具有主键或唯一键。如果没有,就像我的表一样,您可以在另一列上进行分区。以上是关于MySQL 查询:长时间处于“init”状态的主要内容,如果未能解决你的问题,请参考以下文章
MySQL进阶垫脚石:线程长时间处于killed状态怎么破?
如果 Android 设备长时间处于空闲状态,则不会收到推送通知
在 GPS 芯片长时间处于活动状态后,Android 设备需要重启/重启才能获取 GPS 位置
许多长时间运行的 Apache 进程处于 READ 状态,一段时间后没有请求