我已经在共享主机上工作了很长时间,几个月来一直运行相同的查询没有问题。突然之间,我收到了一封来自我的主机的电子邮件,说他们在我的数据库上放了一个块,因为“发现这个数据库耗费了大量的处理器时间”。我不明白为什么现在呢?所以无论如何我想也许我需要去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
我知道它没有格式化,所以你可以轻松阅读...我不知道如何把它变成一张桌子而不是这里。遗憾!
答案 0 :(得分:1)
有很多事情可能会影响您的查询,使用EXPLAIN
应该揭示对性能影响最大的问题。
但这里有一些一般性指导原则:
LEFT JOIN
然后过滤WHERE
子句上的具体值?使用外连接将增加稍后将被WHERE
子句上的条件过滤和丢弃的行数。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
结果来看,这非常糟糕:
ALL
下的所有type
值都表示MySQL正在进行全表扫描,即必须读取表中的所有行。这非常非常糟糕。NULL
下的possible_keys
没有显示可用的索引。这是MySQL进行表扫描的另一个原因,它必须查看所有连接的行以过滤所需的行。我会说最糟糕的部分是按city
和state
过滤。如果没有连接,那么这两个字段在TeacherDriveCity
中的简单查找将需要MySQL查看可能的7489行。更不用说两个字段都包含字符串。
快速修复
在JOIN
和WHERE
子句中使用的所有列上添加索引。但请考虑不要在LEFT
和TeacherBiography
之外的表上使用外部(TeacherPhilosophy
)联接。