0

我已经使用共享主机很长时间了,并且几个月来一直在运行相同的查询而没有问题。突然间,我收到一封来自主机的电子邮件,说他们在我的数据库上设置了一个块,因为“发现数据库消耗了过多的处理器时间”。我不明白为什么现在?所以无论如何我想也许我需要去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() 的 order 很慢,但我不认为它会在一次调用中使整个服务器崩溃。我在这里错过了什么吗?任何帮助,将不胜感激!我的网站关闭了 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

我知道它没有格式化,因此您可以轻松阅读...我不知道如何将其制成表格以及此处不知道的内容。对不起!

4

1 回答 1

1

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

但这里有一些一般准则:

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

现在想想这个:

数据库处理连接的方式通常是组合每个表中的所有行,然后丢弃不满足连接条件的组合。这就是为什么在连接字段上有索引非常重要的原因,因为这样组合使用索引键而不是包含两个表中所有字段的临时表。这也是不使用 aLEFT 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的结果来看,情况非常糟糕:

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

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

快速解决

JOIN在andWHERE子句中使用的所有列上添加索引。但请考虑不要在和LEFT以外的表上使用外部 ( ) 连接。TeacherBiographyTeacherPhilosophy

于 2012-10-12T22:29:44.347 回答