2015-05-23 80 views
7

我一直在阅读其他帖子,但我没有设法修复我的查询。避免与INNER JOIN +排序

使用DESC命令查询速度慢了20倍,我必须改进它。 这是查询:

SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title 
FROM posts 
INNER JOIN follow ON posts.post_b_id = follow.board_id 
INNER JOIN boards ON posts.post_b_id = boards.board_id 
WHERE follow.user_id =1 
ORDER BY posts.post_id DESC 
LIMIT 10 

而这些表(更新):

CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT, 
`post_b_id` int(11) unsigned NOT NULL, 
`post_title` varchar(50) COLLATE utf8_bin NOT NULL, 
`post_cont` text COLLATE utf8_bin NOT NULL, 
`post_mintxt` varchar(255) COLLATE utf8_bin NOT NULL, 
`post_type` char(3) COLLATE utf8_bin NOT NULL, 
`thumb` varchar(200) COLLATE utf8_bin NOT NULL, 
`post_user` varchar(16) COLLATE utf8_bin NOT NULL, 
`published` enum('0','1') COLLATE utf8_bin NOT NULL, 
`post_ip` varchar(94) COLLATE utf8_bin NOT NULL, 
`post_ip_dat` int(11) unsigned NOT NULL, 
`post_up` int(10) unsigned NOT NULL DEFAULT '0', 
`post_down` int(10) unsigned NOT NULL DEFAULT '0', 
PRIMARY KEY (`post_id`), 
KEY `post_b_id` (`post_b_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=405 ; 

CREATE TABLE IF NOT EXISTS `boards` (
`board_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`board_title_l` varchar(19) COLLATE utf8_bin NOT NULL, 
`board_user_id` int(10) unsigned NOT NULL, 
`board_title` varchar(19) COLLATE utf8_bin NOT NULL, 
`board_user` varchar(16) COLLATE utf8_bin NOT NULL, 
`board_txt` tinyint(1) unsigned NOT NULL, 
`board_img` tinyint(1) unsigned NOT NULL, 
`board_vid` tinyint(1) unsigned NOT NULL, 
`board_desc` varchar(100) COLLATE utf8_bin NOT NULL, 
`board_mod_p` tinyint(3) unsigned NOT NULL DEFAULT '0', 
`board_ip` varchar(94) COLLATE utf8_bin NOT NULL, 
`board_dat_ip` int(11) unsigned NOT NULL, 
PRIMARY KEY (`board_id`), 
UNIQUE KEY `board_title_l` (`board_title_l`), 
KEY `board_user_id` (`board_user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=89 ; 

CREATE TABLE IF NOT EXISTS `follow` (
`user_id` int(10) unsigned NOT NULL, 
`board_id` int(10) unsigned NOT NULL, 
PRIMARY KEY (`user_id`,`board_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 

使用默认ASC为了它仅使用指数并在,与DESC使用索引,其中,临时和文件。

id select_type table type possible_keys key   key_len ref    rows filtered Extra 
1 SIMPLE  follow ref  user_id   user_id  4  const    2  100.00  Using index; Using temporary; Using filesort 
1 SIMPLE  boards eq_ref PRIMARY   PRIMARY  4 xxxx.follow.board_id 1  100.00 
1 SIMPLE  posts ref  post_b_id  post_b_id 4 xxxx.boards.board_id 3  100.00  Using where 

我怎样才能使查询接收DESC为了结果不文件排序和暂时的。

更新:我做了一个新的查询,没有临时或文件排序,但类型:指数,过滤:7340.00。几乎与ASC命令一样快,如果帖子在表格末尾,但是如果正在搜索的帖子在开头,则速度会变慢。所以似乎更好,但这还不够。

SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title 
FROM posts INNER JOIN boards ON posts.post_b_id = boards.board_id 
WHERE posts.post_b_id 
IN (
SELECT follow.board_id 
FROM follow 
WHERE follow.user_id = 1 
) 
ORDER BY posts.post_id DESC 
LIMIT 10 

解释:

id select_type  table type possible_keys key  key_len ref    rows filtered Extra 
1 PRIMARY   posts index post_b_id  PRIMARY  8  NULL    10  7340.00 Using where 
1 PRIMARY   boards eq_ref PRIMARY   PRIMARY  4 xxxx.posts.post_b_id 1  100.00 
2 DEPENDENT SUBQUERY follow eq_ref user_id   user_id  8  const,func   1  100.00  Using index 

UPDATE:dened's answer解释查询:

id select_type table type possible_keys key  key_len ref    rows filtered Extra 
1 PRIMARY  <derived2>ALL NULL    NULL  NULL  NULL    10  100.00 
1 PRIMARY  posts eq_ref PRIMARY,post_b_id PRIMARY 4  sq.post_id  1  100.00  
1 PRIMARY  boards eq_ref PRIMARY   PRIMARY 4 xxxx.posts.post_b_id 1  100.00 
2 DERIVED  follow ref  PRIMARY   PRIMARY 4       1  100.00  Using index; Using temporary; Using filesort 
2 DERIVED  posts ref  post_b_id  post_b_id 4 xxxx.follow.board_id 6  100.00  Using index 

时报:

Original query no order (ASC):    0.187500 seconds 
Original query DESC:      2.812500 seconds 
Second query posts at the end (DESC):  0.218750 seconds 
Second query posts at the beginning (DESC): 3.293750 seconds 
dened's query DESC:       0.421875 seconds 
dened's query no order (ASC):    0.323750 seconds 

有意思的是,如果我加ORDER BY ASC就是DESC慢。

改变表顺序将是一种上帝的方式,但正如我在评论中所说,我无法做到这一点。

+1

这看起来不错。不知道你能做什么:-( – Strawberry

+0

你可以在post_id上添加一个降序索引 – Martijn

+0

“InnoDB总是根据聚集索引对表行进行排序”我试着用ALTER TABLE'posts' ORDER BY'post_id' DESC;并且它保持ASC无论如何,其他引擎可以具有DESC顺序,但是如果插入或删除任何行,它不会维护顺序 – Vixxs

回答

1

您可以通过移动所有的过滤工作提高到一个子查询什么只访问索引帮助MySQL优化(操纵指数通常比操纵其他数据快得多),在最外层查询和获取数据的其余部分:

SELECT posts.post_id, 
     posts.post_b_id, 
     posts.post_title, 
     posts.post_cont, 
     posts.thumb, 
     posts.post_user, 
     boards.board_title_l, 
     boards.board_title 
FROM (SELECT post_id 
     FROM posts 
       JOIN follow 
       ON posts.post_b_id = follow.board_id 
     WHERE follow.user_id = 1 
     ORDER BY post_id DESC 
     LIMIT 10) sq 
     JOIN posts 
     ON posts.post_id = sq.post_id 
     JOIN boards 
     ON boards.board_id = posts.post_b_id 

请注意,我从外部查询中省略了ORDER BY posts.post_id DESC,因为通常在代码中排序最终结果比使用MySQL查询进行排序要快(MySQL通常使用文件夹)。

P.S.您可以用主键替换follow表中的唯一密钥。

+0

按照主键制作。经过测试,你的查询速度稳定,无论帖子在哪里,速度都不及原始查询无顺序,但也许这是不可能的。 – Vixxs

+0

@Vixxs,我稍微改变了这个查询。这应该会更快一点。这个版本假设'posts'的主键只是'(post_id)',但不是'(post_id,post_b_id)'。 – dened

+0

@Vixxs,请不要忘记标记答案为接受,如果它有助于解决您的问题。见[这里](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)。如果它对你有帮助,你也可以提出任何答案。请参阅[这里](http://stackoverflow.com/help/privileges/vote-up)。 – dened

0

增加sort_buffer_size参数将增加MySQL在使用临时磁盘文件之前使用的内存量,并且应该有相当大的帮助。

+0

不幸的是,所有这些表都小于250kb在一起,我增加d sort_buffer_size和其他缓冲区(从1mb)到16mb,在xampp上,结果相同。 – Vixxs