2012-01-04 124 views
6

使用試驗和錯誤我發現,從下面的查詢中刪除連接時,它運行速度快30倍。有人可以解釋爲什麼會這樣,並且如果可以優化查詢以包含沒有性能影響的額外連接。使用昂貴的INNER JOIN優化MySQL查詢

這是解釋的屏幕截圖,它顯示索引未被用於uesr_groups表。

enter image description here

http://i.imgur.com/9VDuV.png

這是原始查詢:

SELECT `comments`.`comment_id`, `comments`.`comment_html`, `comments`.`comment_time_added`, `comments`.`comment_has_attachments`, `users`.`user_name`, `users`.`user_id`, `users`.`user_comments_count`, `users`.`user_time_registered`, `users`.`user_time_last_active`, `user_profile`.`user_avatar`, `user_profile`.`user_signature_html`, `user_groups`.`user_group_icon`, `user_groups`.`user_group_name` 
FROM (`comments`) 
INNER JOIN `users` ON `comments`.`comment_user_id` = `users`.`user_id` 
INNER JOIN `user_profile` ON `users`.`user_id` = `user_profile`.`user_id` 
INNER JOIN `user_groups` ON `users`.`user_group_id` = `user_groups`.`user_group_id` 
WHERE `comments`.`comment_enabled` = 1 
AND `comments`.`comment_content_id` = 12 
ORDER BY `comments`.`comment_time_added` ASC 
LIMIT 20 

如果我刪除 「user_groups」 加入,則查詢運行快30倍,如上所述。

SELECT `comments`.`comment_id`, `comments`.`comment_html`, `comments`.`comment_time_added`, `comments`.`comment_has_attachments`, `users`.`user_name`, `users`.`user_id`, `users`.`user_comments_count`, `users`.`user_time_registered`, `users`.`user_time_last_active`, `user_profile`.`user_avatar`, `user_profile`.`user_signature_html` 
FROM (`comments`) 
INNER JOIN `users` ON `comments`.`comment_user_id` = `users`.`user_id` 
INNER JOIN `user_profile` ON `users`.`user_id` = `user_profile`.`user_id` 
WHERE `comments`.`comment_enabled` = 1 
AND `comments`.`comment_content_id` = 12 
ORDER BY `comments`.`comment_time_added` ASC 
LIMIT 20 

我的表是下面,任何人都可以提供任何深入瞭解如何避免包括user_groups表性能的下降?

-- 
-- Table structure for table `comments` 
-- 

CREATE TABLE IF NOT EXISTS `comments` (
    `comment_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `comment_content_id` int(10) unsigned NOT NULL, 
    `comment_user_id` mediumint(6) unsigned NOT NULL, 
    `comment_original` text NOT NULL, 
    `comment_html` text NOT NULL, 
    `comment_time_added` int(10) unsigned NOT NULL, 
    `comment_time_updated` int(10) unsigned NOT NULL, 
    `comment_enabled` tinyint(1) NOT NULL DEFAULT '0', 
    `comment_is_spam` tinyint(1) NOT NULL DEFAULT '0', 
    `comment_has_attachments` tinyint(1) unsigned NOT NULL, 
    `comment_has_edits` tinyint(1) NOT NULL, 
    PRIMARY KEY (`comment_id`), 
    KEY `comment_user_id` (`comment_user_id`), 
    KEY `comment_content_id` (`comment_content_id`), 
    KEY `comment_is_spam` (`comment_is_spam`), 
    KEY `comment_enabled` (`comment_enabled`), 
    KEY `comment_time_updated` (`comment_time_updated`), 
    KEY `comment_time_added` (`comment_time_added`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=352 ; 

-- -------------------------------------------------------- 

-- 
-- Table structure for table `users` 
-- 

CREATE TABLE IF NOT EXISTS `users` (
    `user_id` mediumint(6) unsigned NOT NULL AUTO_INCREMENT, 
    `user_ipb_id` int(10) unsigned DEFAULT NULL, 
    `user_activated` tinyint(1) NOT NULL DEFAULT '0', 
    `user_name` varchar(64) CHARACTER SET latin1 NOT NULL, 
    `user_email` varchar(255) NOT NULL, 
    `user_password` varchar(40) NOT NULL, 
    `user_content_count` int(10) unsigned NOT NULL DEFAULT '0', 
    `user_comments_count` int(10) unsigned NOT NULL DEFAULT '0', 
    `user_salt` varchar(8) NOT NULL, 
    `user_api_key` varchar(32) NOT NULL, 
    `user_auth_key` varchar(32) DEFAULT NULL, 
    `user_paypal_key` varchar(32) DEFAULT NULL, 
    `user_timezone_id` smallint(3) unsigned NOT NULL, 
    `user_group_id` tinyint(3) unsigned NOT NULL, 
    `user_custom_permission_mask_id` tinyint(3) unsigned DEFAULT NULL, 
    `user_lang_id` tinyint(2) unsigned NOT NULL, 
    `user_time_registered` int(10) unsigned NOT NULL, 
    `user_time_last_active` int(10) unsigned NOT NULL 
    PRIMARY KEY (`user_id`), 
    UNIQUE KEY `user_email` (`user_email`), 
    KEY `user_group_id` (`user_group_id`), 
    KEY `user_auth_key` (`user_auth_key`), 
    KEY `user_api_key` (`user_api_key`), 
    KEY `user_custom_permission_mask_id` (`user_custom_permission_mask_id`), 
    KEY `user_time_last_active` (`user_time_last_active`), 
    KEY `user_paypal_key` (`user_paypal_key`), 
    KEY `user_name` (`user_name`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=33 ; 

-- -------------------------------------------------------- 

-- 
-- Table structure for table `user_groups` 
-- 

CREATE TABLE IF NOT EXISTS `user_groups` (
    `user_group_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT, 
    `user_group_name` varchar(32) NOT NULL, 
    `user_group_permission_mask_id` tinyint(3) unsigned NOT NULL, 
    `user_group_icon` varchar(32) DEFAULT NULL, 
    PRIMARY KEY (`user_group_id`), 
    KEY `user_group_permission_mask_id` (`user_group_permission_mask_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ; 

-- -------------------------------------------------------- 

-- 
-- Table structure for table `user_profile` 
-- 

CREATE TABLE IF NOT EXISTS `user_profile` (
    `user_id` mediumint(8) unsigned NOT NULL, 
    `user_signature_original` text, 
    `user_signature_html` text, 
    `user_avatar` varchar(64) DEFAULT NULL, 
    `user_steam_id` varchar(64) DEFAULT NULL, 
    `user_ps_id` varchar(16) DEFAULT NULL, 
    `user_xbox_id` varchar(64) DEFAULT NULL, 
    `user_wii_id` varchar(64) DEFAULT NULL, 
    PRIMARY KEY (`user_id`), 
    KEY `user_steam_id` (`user_steam_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 
+2

值得運行「優化」(http://dev.mysql.com/doc/refman/5.0/en/optimize-table.html) - 尤其是如果您的表格增長迅速。 – 2012-01-04 13:27:12

+0

這仍然是在開發環境中,所以表格並不是很大。然而我試過了,它似乎有了很大的變化,現在正在使用索引並且運行得更快。 – robjbrain 2012-01-04 13:31:48

+0

您是否願意提供有關優化程序的使用和實用性的更多信息,並定期在實時環境中運行它。如果你添加一個完整的答案,而不只是一個評論,我將能夠接受你的答案:) – robjbrain 2012-01-04 13:33:15

回答

6

大多數數據庫引擎計算出它們的查詢計劃 - 例如,如果一個表有一個小的行數,去索引比索引更快。這些統計數據在「正常」操作期間保持 - 例如插入,更新和刪除 - 但在表定義更改或執行批量插入時可能會不同步。

如果您在查詢計劃中看到意外行爲,則可以強制數據庫更新其統計信息;在MySQL中,您可以使用Optimize Table--它可以完成所有工作,包括對錶格本身進行重新排序,或者只更新索引的Analyze Table

這在生產環境中很難做到,因爲兩個操作都鎖定表;如果您可以談判維護窗口,那麼這是解決問題的最簡單方法。

「優化表」的性能值得關注 - 在精心指定的硬件上,對於「正常」大小的表(高達低數百萬條記錄,只有少量指標)應該只需幾秒鐘。這可能意味着您可以擁有一個「非正式」維護時段 - 您不會將應用程序脫機,您只需接受某些用戶在運行腳本時性能會降低。

2

MySQL有一個EXPLAIN功能,這將有助於你理解查詢:

$ mysql 
> EXPLAIN SELECT `comments`.`comment_id`, `comments`.`comment_html`,`comments`.`comment_time_added`, `comments`.`comment_has_attachments`, `users`.`user_name`, `users`.`user_id`, `users`.`user_comments_count`, `users`.`user_time_registered`, `users`.`user_time_last_active`, `user_profile`.`user_avatar`, `user_profile`.`user_signature_html` 
    FROM (`comments`) 
    INNER JOIN `users` ON `comments`.`comment_user_id` = `users`.`user_id` 
    INNER JOIN `user_profile` ON `users`.`user_id` = `user_profile`.`user_id` 
    WHERE `comments`.`comment_enabled` = 1 
    AND `comments`.`comment_content_id` = 12 
    ORDER BY `comments`.`comment_time_added` ASC 
    LIMIT 20 

MySQL的可能僅僅是丟失或跳過的索引。

您可以瞭解更多有關從simpler explanation here, (ignore the fact that it's on a Java site.)

比數據的可能量,或過時的或不完整的指數比較瞭解的EXPLAIN這裏from the documentation (a little hard-core),或更好的輸出又被這意味着MySQL是錯誤地做一個表掃描。當您看到表掃描順序文件夾時,您通常可以很容易地看到哪個字段缺少索引或索引不可用。

+0

是的,這是解釋的輸出:http://i.imgur.com/9VDuV.png – robjbrain 2012-01-04 13:07:14

+0

正如你可以看到它缺少用戶組的索引,可能是因爲數據很小(只有7個行),但它不可能比這更大。 – robjbrain 2012-01-04 13:08:27

+0

這是來自with,還是沒有'JOIN'的解釋?我會考慮避免臨時表的方法,或者調整MySQL引擎以提高效率,[來自stackexchange的相關帖子可能會對您有所幫助](http://dba.stackexchange.com/questions/2161/avoiding-臨時表,同時按不同列表排序) – 2012-01-04 13:31:14

1

你可以試試這個(你可以刪除加入user_group)。它可以更快的情況下,如果查詢從comments表中檢索小的數據集:基於關於表的統計信息

SELECT 
    comments.comment_id, comments.comment_html, comments.comment_time_added, comments.comment_has_attachments, users.user_name, users.user_id, users.user_comments_count, users.user_time_registered, users.user_time_last_active, user_profile.user_avatar, user_profile.user_signature_html, user_groups.user_group_icon, user_groups.user_group_name 
FROM 
    (select * from comments where comment_content_id = 12 and active = 1) comments 
     INNER JOIN users u ON c.comment_user_id = users.user_id 
     INNER JOIN user_profile ON users.user_id = user_profile.user_id 
     INNER JOIN user_groups ON users.user_group_id = user_groups.user_group_id 
ORDER BY comments.comment_time_added ASC 
LIMIT 20 
0

嘗試對非空關係使用左連接。

看起來,因爲內部連接總是對稱的,所以mysql會重新排列連接以便首先使用最好看的(通常是最小的)表格。

由於左連接並不總是對稱的,mysql不會對它們重新排序,因此您可以使用它們來強制執行表順序。但是,如果留有非空字段,而內部是相同的,則結果不會更改。

表格順序將決定使用哪些可能會對性能產生重大影響的標記。