【问题标题】:Help speeding up MySql Query帮助加快 MySql 查询
【发布时间】:2011-03-07 02:13:24
【问题描述】:

如何加快这个 mysql 查询的速度?

   SET @rank = 0;  

  UPDATE dbTable 
     SET rank_fd = @rank := @rank + 1 
ORDER BY fd2 DESC, fd3 DESC;

此查询更新约 300,000 个表行。它只是花费了太长时间......简单的控制面板(涡轮面板?)说mysqld只使用8-9%的cpu。我知道这总是正确的,但是这个查询在我的 Core i7 机器上花费的时间几乎没有。我认为在我的 P4 专用服务器上大约需要 5-10 分钟......这不会是一个问题,除非我必须为不同的值多次执行此操作。它还使其他 mysql 操作极其缓慢(几乎挂起),从而严重影响网站性能。

软件:

  • CentOS 5.4 版(最终版),
  • 简单控制面板版本 1.4.8,
  • PHP 5.2.16
  • MySQL 客户端版本:5.1.52

硬件:

  • 英特尔奔腾 4 CPU 3.0GHz
  • 2GB 内存

更多信息(添加于 2011 年 3 月 6 日 @ 10:06PM CST):

//Core i7 920 @ 2.6GHz, 6GB Ram
UPDATE dbTable SET rank_fd =999999999;
#275037 row(s) affected. ( Query took 7.0708 sec )

SET @rank =0;
#Your SQL query has been executed successfully ( Query took 0.0003 sec )

UPDATE dbTable SET rank_fd = @rank := @rank +1 ORDER BY fd2 DESC, fd3 DESC ;
#275037 row(s) affected. ( Query took 9.9931 sec )



//P4 3.0GHz, 2GB Ram
UPDATE dbTable SET rank_fd =999999999;
#Affected rows: 291468 (Query took 8.2165 sec)

SET @rank =0;
#Your SQL query has been executed successfully (Query took 0.0002 sec)

UPDATE dbTable SET rank_fd = @rank := @rank +1 ORDER BY fd2 DESC, fd3 DESC ;
#Affected rows: 291469 (Query took 305.2104 sec)  

更多信息(添加于 2011 年 3 月 7 日 @ 下午 6:37 CST):

我有一些新信息。如果我在 P4 上做一个 select 语句:

SET @rank =0;
SELECT @rank := @rank +1 AS rank_fd FROM dbTable ORDER BY fd2 DESC, fd3 DESC LIMIT 0, 300000;  
#Showing rows 0 - 29 (292,437 total, Query took 3.0448 sec)

计算一切只需要 3 秒。 no-calc 批量更新语句只需要 8 秒。什么是所有额外的工作导致它在我原来的陈述中超过 300 秒。有没有办法在不涉及 PHP 的 select calc 语句之后捕获更新。我之所以这么说,是因为如果我在 PHP 中循环它,它会比原来的语句花费更长的时间。

感谢到目前为止的所有帮助!!!

【问题讨论】:

  • 这种查询速度很慢。将整个表转储到外部应用程序,在那里更新,在 db 上截断并将其从外部应用程序插入到 db 可能会更快。你需要 rank_fd 做什么?
  • @PiZzL3 :您忽略了重要信息:即磁盘 I/O 子系统....
  • 是的,这种查询可能会受到 I/O 限制。
  • 我相信硬盘只是普通的7200rpm,你可以在任何商店买到,没什么特别的,没有raid配置。如果我可以以某种方式为您运行基准测试,请告诉我如何。我对服务器管理了解不多。
  • @Michas:rank_fd 是由 ORDER BY 子句确定的排名(排行榜位置)。我缓存了这个,因为每次玩家统计数据发生变化时更新成本太高。

标签: php mysql sql


【解决方案1】:

您正在修改表格中的每一行,当然会很慢。根据rank_fd 的使用方式,您可能只需在fd2fd3 上放置索引并在运行时计算排名即可。

【讨论】:

  • 表,不是数据库。但是,是的,300+K 条记录并非易事
  • 好吧,我认为索引 fd2/3 不会有帮助。在单独的查询中订购这些几乎不需要任何时间(所以我认为在这个查询中也不会花费很长时间)。是否有不同的方法来运行该查询以使其运行得更快(例如不同的查询)?它还必须使用最少的 php 来完成,因为这往往会在这些类型的计算中变成一个巨大的性能瓶颈。
  • 在运行时计算它似乎不是一个选项,因为它必须跨 30 种其他类别类型计算。而且我不确定索引所有表字段会如何影响性能。我认为每次更新字段时,索引也必须更新。
  • 重点是MySQL能够在每次插入时增量更新索引;然而,您正在尝试每隔一段时间进行一次批量更新。列索引有助于 MySQL 进行排序,因此您可以通过查询降低排序。
  • 有没有办法防止挂机? 8-9% 的 cpu 并且挂起非常令人沮丧。
【解决方案2】:

您可以使用存储过程和临时表来加快排名过程。

以下示例使用包含 150 万行的玩家得分表,其中 500K 玩家得分超过 3 轮 (500K * 3),并在大约 50 分钟内更新第 1 轮(500K 行)的排名。 5 秒。

希望这会有所帮助:)

示例表和存储过程

drop table if exists player_scores;
create table player_scores
(
round_id smallint unsigned not null,
player_id int unsigned not null,
score_1 int unsigned not null default 0,
score_2 int unsigned not null default 0,
rank int unsigned not null default 0,
primary key (round_id, player_id)
)
engine=myisam;

drop procedure if exists update_player_score_ranking;

delimiter #
create procedure update_player_score_ranking
(
p_round_id smallint unsigned
)
begin

  create table tmp_player_scores engine=memory select 
    round_id, player_id, score_1, score_2, @rank:= @rank + 1 as rank
  from 
    player_scores 
  inner join (select @rank:=0) r
  where
   round_id = p_round_id
  order by 
    score_1 desc, score_2 desc;

  delete from player_scores where round_id = p_round_id;
  insert into player_scores select * from tmp_player_scores;

  drop table if exists tmp_player_scores;

end #

delimiter ;

测试结果:

select count(*) from player_scores;
+----------+
| count(*) |
+----------+
|  1500000 |
+----------+
1 row in set (0.00 sec)

select count(*) from player_scores where round_id = 1;
+----------+
| count(*) |
+----------+
|   500000 |
+----------+
1 row in set (0.07 sec)

select * from player_scores where round_id = 1 order by score_1 desc, score_2 desc limit 5;
+----------+-----------+---------+---------+------+
| round_id | player_id | score_1 | score_2 | rank |
+----------+-----------+---------+---------+------+
|        1 |    456937 |   65534 |   49579 |    0 |
|        1 |     72439 |   65534 |   44537 |    0 |
|        1 |     16427 |   65534 |   43045 |    0 |
|        1 |    259871 |   65534 |   32095 |    0 |
|        1 |    324702 |   65534 |   15227 |    0 |
+----------+-----------+---------+---------+------+
5 rows in set (0.71 sec)


call update_player_score_ranking(1);
Query OK, 0 rows affected (5.57 sec)

select * from player_scores where round_id = 1 order by rank limit 5;
+----------+-----------+---------+---------+------+
| round_id | player_id | score_1 | score_2 | rank |
+----------+-----------+---------+---------+------+
|        1 |    456937 |   65534 |   49579 |    1 |
|        1 |     72439 |   65534 |   44537 |    2 |
|        1 |     16427 |   65534 |   43045 |    3 |
|        1 |    259871 |   65534 |   32095 |    4 |
|        1 |    324702 |   65534 |   15227 |    5 |
+----------+-----------+---------+---------+------+
5 rows in set (1.29 sec)

【讨论】:

  • 哇,有很多东西我还不太明白......不过我会调查一下。谢谢!