服务器崩溃 - 优化Mysql查询

时间:2012-10-12 21:49:45

标签: mysql webserver

我已经在共享主机上工作了很长时间,几个月来一直运行相同的查询没有问题。突然之间,我收到了一封来自我的主机的电子邮件,说他们在我的数据库上放了一个块,因为“发现这个数据库耗费了大量的处理器时间”。我不明白为什么现在呢?所以无论如何我想也许我需要去VPS。试图让我的网站上一个VPS,并立即整个网站有一个内存不足的限制错误,并已崩溃。有一段时间,连接在到达网站时超时了。

我的表都不是非常大,不超过10,000行。以下是我原来的主人寄给我的一些事情:

DB_USER: my_user -- TOTAL_CONNECTIONS: 37 -- CONNECTED_TIME: 4906 --
CPU_TIME: 1920 -- TABLE_ROW_READS: 79999077 -- SELECT_COMMANDS: 214 --
UPDATE_COMMANDS: -- BUSY_TIME: 4879 -- BYTES_SENT: 252225 --
BYTES_RECEIVED: 105418 -- WAIT_TIME (IO): 2959 

表行读取79,999,077 ??说真的是如何运作的。我的桌子都很小。我没有得到很多流量。也许每天100-150人。但这似乎并不重要,因为当我把它放在新服务器上时,该网站完全死了。这是电子邮件中的一个查询,说我的数据库已关闭。是真的只有这一个查询杀了我吗?几个月来一直没有问题:

SELECT `Person`.`first_name`, `Person`.`last_name`,`Person`.`id`,`Upload`.`path`,`TeacherBiography`.`final_biography`, `TeacherPhilosophy`.`final_philosophy`
FROM `my_database`.`people` AS `Person`
LEFT JOIN `my_database`.`teacher_drive_cities` AS `TeacherDriveCity`
    ON (`TeacherDriveCity`.`person_id` = `Person`.`id`)
LEFT JOIN `my_database`.`instruments_people` AS `InstrumentsPerson`
    ON (`InstrumentsPerson`.`person_id` = `Person`.`id`)
LEFT JOIN `my_database`.`instruments` AS `Instrument`
    ON (`Instrument`.`id` = `InstrumentsPerson`.`instrument_id`)
LEFT JOIN `my_database`.`teachers` AS `Teacher`
    ON (`Teacher`.`person_id` = `Person`.`id`)
LEFT JOIN `my_database`.`uploads` AS `Upload`
    ON (`Upload`.`person_id` = `Person`.`id`)
LEFT JOIN `my_database`.`teacher_biographies` AS `TeacherBiography`
    ON (`TeacherBiography`.`person_id` = `Person`.`id`)
LEFT JOIN `my_database`.`teacher_philosophies` AS `TeacherPhilosophy`
    ON (`TeacherPhilosophy`.`person_id` = `Person`.`id`)
WHERE `TeacherDriveCit! y`.`city` = 'Dana Point'
    AND `TeacherDriveCity`.`state` = 'ca'
    AND `Instrument`.`instrument` = 'saxophone'
    AND `Teacher`.`status` = 6
    AND `Upload`.`description` = 'profile_picture'
    AND `TeacherBiography`.`final_biography` IS NOT NULL
    AND `TeacherPhilosophy`.`final_philosophy` IS NOT NULL
GROUP BY `Person`.`id`
ORDER BY RAND() ASC
LIMIT 3 

我听说rand()的命令很慢,但我认为它不会在一次通话中使整个服务器崩溃。我在这里错过了什么吗?任何帮助,将不胜感激!我的网站已经停机了24小时,我靠它的收入过世。谢谢!

编辑:以下是有关查询的说明:

1   SIMPLE  TeacherBiography    ALL NULL    NULL    NULL    NULL    41    Using where; Using temporary; Using filesort
1   SIMPLE  TeacherPhilosophy   ALL NULL    NULL    NULL    NULL    41  Using where; Using join buffer
1   SIMPLE  Upload              ALL NULL    NULL    NULL    NULL    166 Using where; Using join buffer
1   SIMPLE  Teacher             ALL NULL    NULL    NULL    NULL    381 Using where; Using join buffer
1   SIMPLE  InstrumentsPerson   ALL NULL    NULL    NULL    NULL    647 Using join buffer
1   SIMPLE  Instrument  eq_ref  PRIMARY PRIMARY 4   yml_yml.InstrumentsPerson.instrument_id 1   Using where
1   SIMPLE  Person  eq_ref  PRIMARY PRIMARY 4   yml_yml.TeacherPhilosophy.person_id     1   Using where
1   SIMPLE  TeacherDriveCity    ALL NULL    NULL    NULL    NULL    7489    Using where; Using join buffer

我知道它没有格式化,所以你可以轻松阅读...我不知道如何把它变成一张桌子而不是这里。遗憾!

1 个答案:

答案 0 :(得分:1)

有很多事情可能会影响您的查询,使用EXPLAIN应该揭示对性能影响最大的问题。

但这里有一些一般性指导原则:

  • 确保所有表都有用于连接表的字段的索引。
  • 为什么使用LEFT JOIN然后过滤WHERE子句上的具体值?使用外连接将增加稍后将被WHERE子句上的条件过滤和丢弃的行数。
  • 确保索引覆盖了用于过滤的所有列。但是,为什么你要按文字值过滤?即使索引覆盖了city,state和instrument字段,比较字符串也需要更多的处理来比较整数。
  • 如果您随机订购,为什么要按Person.id分组?由于表连接,分组强制排序操作很可能需要临时表。

现在也想一想:

数据库进程连接的方式通常是组合每个表中的所有行,然后丢弃不满足连接条件的组合。这就是为什么在连接字段上有索引非常重要的原因,因为这种组合使用索引键而不是临时表和两个表中的所有字段。这也是不使用LEFT JOIN然后丢弃之前可能已被丢弃的行组合(在WHERE子句中)的原因。

要了解此查询正在做什么,以及为什么它影响性能如此糟糕地计算:

Total rows to filter = Number of rows in table Person
 * Number of rows in table TeacherDriveCity
 * Number of rows in table Instrument
 * Number of rows in table Teacher
 * Number of rows in table Upload
 * Number of rows in table TeacherBiography
 * Number of rows in table TeacherPhilosophy

我很确定这将是一个非常大的数字。

EXPLAIN解释

从发布的EXPLAIN结果来看,这非常糟糕:

  • 如上所述,join ALL下的所有type值都表示MySQL正在进行全表扫描,即必须读取表中的所有行。这非常非常糟糕。
  • 对于大多数连接条件,NULL下的possible_keys没有显示可用的索引。这是MySQL进行表扫描的另一个原因,它必须查看所有连接的行以过滤所需的行。

我会说最糟糕的部分是按citystate过滤。如果没有连接,那么这两个字段在TeacherDriveCity中的简单查找将需要MySQL查看可能的7489行。更不用说两个字段都包含字符串。

快速修复

JOINWHERE子句中使用的所有列上添加索引。但请考虑不要在LEFTTeacherBiography之外的表上使用外部(TeacherPhilosophy)联接。