今天早上,我开始在我管理的一个MySQL服务器上收到负载警告。SHOW FULL PROCESSLIST发现,罪魁祸首是以下(写得不好)查询:
SELECT * FROM movies, showtimes
WHERE site='5' AND
(showtimes.movie = movies.id OR always='true') AND
((showdate>='2012-4-11' AND showdate<'2012-04-18') OR always='true')\
GROUP BY movies.id ORDER BY listorder我不知道这是谁写的。看起来它就在我几年前设计的5+数据库上,自从我上次接触它以来,不知名的人一直在修改它。有关表格如下:
CREATE TABLE `movies` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(128) NOT NULL default '',
`rating` varchar(5) NOT NULL default '',
`description` varchar(255) NOT NULL default '',
`special` varchar(255) NOT NULL default '',
`url` varchar(64) NOT NULL default '',
`poster` varchar(255) NOT NULL default '',
`runtime` varchar(20) NOT NULL default '',
`listorder` tinyint(4) NOT NULL default '0',
`comsoon` enum('false','true') NOT NULL default 'false',
`always` enum('false','true') NOT NULL default 'false',
`site` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `rating` (`rating`),
KEY `site` (`site`),
KEY `comsoon` (`comsoon`),
KEY `always` (`always`),
KEY `listorder` (`listorder`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `showtimes` (
`movie` int(11) NOT NULL default '0',
`showdate` date NOT NULL default '0000-00-00',
`showtime` time NOT NULL default '00:00:00',
PRIMARY KEY (`movie`,`showdate`,`showtime`),
KEY `showdate` (`showdate`),
KEY `showtime` (`showtime`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;当我第一次注意到这种情况时,movies.always甚至没有索引,movies.listorder也没有。不过,我确信这就是问题所在,在这两列上添加索引并没有改变EXPLAIN:
mysql> EXPLAIN SELECT * FROM movies, showtimes WHERE site='5' AND (showtimes.movie = movies.id OR always='true') AND ((showdate>='2012-4-11' AND showdate<'2012-04-18') OR always='true') GROUP BY movies.id ORDER BY listorder;
+----+-------------+-----------+-------+---------------------+---------+---------+------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------------+---------+---------+------+-------+----------------------------------------------+
| 1 | SIMPLE | showtimes | index | PRIMARY,showdate | PRIMARY | 10 | NULL | 93411 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | movies | ALL | PRIMARY,site,always | NULL | NULL | NULL | 25 | Using where |
+----+-------------+-----------+-------+---------------------+---------+---------+------+-------+----------------------------------------------+
2 rows in set (0.00 sec)我尝试重写查询如下:
SELECT * FROM movies, showtimes
WHERE (site='5' AND always='true') OR
(site='5' AND showtimes.movie = movies.id AND showdate>='2012-4-11' AND showdate<'2012-04-18')
GROUP BY movies.id ORDER BY listorder然而,EXPLAIN并没有什么不同。WHy这个查询不使用movies表上的索引吗?我如何向开发人员解释,他们应该重写这个查询,使其不让服务器陷入困境?
发布于 2012-04-11 15:28:22
可以将其重写为UNION。
SELECT * FROM (
(
SELECT movies.* FROM movies
WHERE site='5' AND always='true'
) UNION
(
SELECT movies.* FROM movies
INNER JOIN showtimes ON showtimes.movie=movies.id
WHERE site='5' AND showdate BETWEEN '2012-4-11' AND '2012-04-18'
)
) AS movie_shows
GROUP BY id
ORDER BY listorder我还没有测试过上面的内容,但是我很好奇EXPLAIN的结果
您的always属性在以下两方面失败:
OR。这意味着它无论如何都要做一次表格扫描才能匹配这两种条件。发布于 2012-04-11 15:38:02
您也可以尝试这样做(通过以下方式避免分组):
SELECT movies.*
FROM movies
WHERE site = 5
AND always = 'true'
OR site = 5
AND always = 'false'
AND EXISTS
( SELECT *
FROM showtimes
WHERE movies.id = showtimes.movie
AND showdate >= '2012-04-11'
AND showdate < '2012-04-18'
)
ORDER BY listorderhttps://dba.stackexchange.com/questions/16352
复制相似问题