2016-06-07 43 views
1

在MySQL慢查詢日誌,我有以下查詢:尋找最佳的指數爲這個MySQL查詢

SELECT * FROM `news_items` 
WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND 
`moderated` = '1' AND `visibility` = '1' 
ORDER BY `views` DESC 
LIMIT 5; 

這裏的結果說明:

+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
| id | select_type | table  | type | possible_keys                   | key | key_len | ref | rows | Extra  | 
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
| 1 | SIMPLE  | news_items | index | feed_id,ctime,ctime_2,feed_id_2,moderated,visibility,feed_id_3,cday_complex,feed_id_4 | views | 4  | NULL | 5 | Using where | 
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

當我手動運行此查詢時,它需要0.00秒,但由於某些原因,它出現在MySQL的慢日誌中,有時需要1-5秒。我相信這發生在服務器處於高負載狀態時。

下面是表結構:

CREATE TABLE IF NOT EXISTS `news_items` (
    `item_id` int(10) NOT NULL, 
    `category_id` int(10) NOT NULL, 
    `source_id` int(10) NOT NULL, 
    `feed_id` int(10) NOT NULL, 
    `title` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `announce` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `content` text CHARACTER SET utf8 NOT NULL, 
    `hyperlink` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `ctime` varchar(11) CHARACTER SET utf8 NOT NULL, 
    `cday` tinyint(2) NOT NULL, 
    `img` varchar(100) CHARACTER SET utf8 NOT NULL, 
    `video` text CHARACTER SET utf8 NOT NULL, 
    `gallery` text CHARACTER SET utf8 NOT NULL, 
    `comments` int(11) NOT NULL DEFAULT '0', 
    `views` int(11) NOT NULL DEFAULT '0', 
    `visibility` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0', 
    `pin` tinyint(1) NOT NULL, 
    `pin_dttm` varchar(10) CHARACTER SET utf8 NOT NULL, 
    `moderated` tinyint(1) NOT NULL 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

命名爲「視圖」索引包含僅1場 - 視圖。 我也有很多其他指標包括(例如):

feed_id + views + visibility + moderated 
moderated + visibility + feed_id + ctime 
moderated + visibility + feed_id + views + ctime 

我使用的字段中提到的順序,因爲這是MySQL的開始使用他們的唯一原因。但是,我從來沒有在EXPLAIN中使用「使用where; using index」。

有關如何使EXPLAIN顯示「使用索引」的任何想法?

+1

基本經驗法則:在決策環境中使用的任何字段(例如,用於「where」,「join」,「order by」等)應該被索引。您的索引看起來多餘。如果一個字段出現在多個索引中,它可能會更好地作爲它自己的單獨索引。例如如果moderated得到改變,你強制改變三個單獨的索引,而不是一個。 –

+0

我會在找到最好的索引時刪除大部分索引。這只是我找到一個好索引所做的一個例子。無論如何,感謝您的評論,我會銘記在心。 – Greg

回答

-1

回答你的問題:「使用索引」意味着MySQL將使用只有索引來滿足你的查詢。要做到這一點,我們需要創建一個「覆蓋」索引(索引「覆蓋」查詢)=索引,其中涵蓋「where」和「order by/group by」以及「select」中的所有字段。但是,您正在做「select *」這樣就不實用。

MySQL在視圖中選擇索引,因爲在查詢中限制爲5。它的確如此1)索引很小2)在這種情況下它可以避免文件。

我相信問題不在於索引而是在引擎= MyISAM。 MyISAM使用表級鎖定,因此如果您更改news_items,它將被鎖定。我建議將錶轉換爲InnoDB。

另一種可能性是,如果表格很大,(視圖)上的索引可能不是最佳選項。

如果您使用的Percona Server,您可以啓用慢日誌詳細選項,並查看查詢計劃的慢查詢如下所述:https://www.percona.com/doc/percona-server/5.5/diagnostics/slow_extended_55.html

+0

嗨亞歷山大!感謝您的回答。所以你認爲「視圖」是這個MyISAM表的最好的索引ATM?它有30k行,80Mbytes。我會嘗試在InnoDB上做一些測試。順便說一下,我使用獅身人面像在這張桌子上進行全文搜索,所以遷移不會是我假設的一個大問題。 – Greg

+0

格雷格,也從MySQL 5.6開始,您可以使用InnoDB和全文索引。使用獅身人面像也是一個好方法。有了80Mb的表格,我可以說視圖上的索引可能是最簡單和最好的。我還建議清理其他索引,因爲其中一些看起來像重複。在MySQL 5.6或更高版本中,您還可以使用性能架構來查找所有未使用的索引。 –

0

如果你有改變存儲引擎InnoDB和創建你正確的綜合指數可以試試這個。第一個查詢只獲取前5行的item_id。完成SELECT後完成限制。因此,它能夠更好地做到這一點沒有任何大的數據,然後從5個疾苦

SELECT idata.* FROM (
    SELECT item_id FROM `news_items` 
    WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND 
    `moderated` = '1' AND `visibility` = '1' 
    ORDER BY `views` DESC 
    LIMIT 5) as i_ids 
LEFT JOIN news_items AS idata ON idata.item_id = i_ids.item_id 
ORDER BY `views` DESC; 
0

只得到孔列如果表「也有很多其他的指標」,爲什麼他們不能在SHOW CREATE TABLE顯示?

有兩種方式

WHERE `ctime` > 1465013901 
    AND `feed_id` IN (1, 2, 9) 
    AND `moderated` = '1' 
    AND `visibility` = '1' 
ORDER BY `views` DESC 

可以使用索引:

  • INDEX(views)希望與期望的5行(見LIMIT)早期出現。
  • INDEX(moderated, visibility, feed_id, ctime)

這最後一個「複合」索引開始與兩列(以任一次序)被比較= constant,然後移動到IN和最後一個「範圍」(ctime > const)。舊版本不會過去IN;新版本將跳過IN值,並利用ctime的範圍。 More discussion

ORDER BY列包含在所有列之前的組合索引中是沒有用的。但是,將views包含在您的案例中將不會有用,因爲ctime上的「範圍」。

Bernd建議的關於「懶惰評估」的提示也將有所幫助。

我同意InnoDB可能會更好。 Conversion tips