2015-05-23 131 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