1. 程式人生 > >MySQL重複索引與冗餘索引

MySQL重複索引與冗餘索引

《高效能MySQL》筆記-冗餘和重複索引

MySQL允許在相同列上建立多個索引,無論是有意的還是無意的。MySQL需要單獨維護重複的索引,並且優化器在優化查詢的時候也需要逐個地進行考慮,這會影響效能。 重複索引是指在相同的列上按照相同的順序建立的相同型別的索引。應該避免這樣建立重複索引,發現以後也應該立即移除。

重複索引 有時會在不經意間建立了重複的索引,例如下面的程式碼:

CREATE TABLE test(
    ID INT NOT NULL PRIMARY KEY,
    A INT NOT NULL,
    B INT NOT NULL,
    UNIQUE
(ID), INDEX(ID) ) ENGINE=InnoDB

一個經驗不足的使用者可能是想建立一個主鍵,先加上唯一限制,然後再加上索引以供查詢使用。事實上,MySQL的唯一限制和主鍵限制都是通過索引實現的,因此,上面的寫法實際上在相同的列上建立了三個重複的索引。通常並沒有理由這樣做,除非是在同一列上建立不同型別的索引來滿足不同的查詢需求。

冗餘索引 冗餘索引和重複索引有一些不同。如果建立了索引(A,B),再建立索引(A)就是冗餘索引,因為這只是前一個索引的字首索引。因此索引(A,B)也可以當做索引(A)來使用(這種冗餘只是對B-Tree索引來說的)。但是如果再建立索引(B,A),則不是冗餘索引,索引(B)也不是,因為B不是索引(A,B)的最左字首。另外,其他不同型別的索引(例如雜湊索引或者全文索引)也不會是B-Tree索引的冗餘索引,而無論覆蓋的索引列是什麼。 冗餘索引通常發生在為表新增新索引的時候。例如,有人可能會增加一個新的索引(A,B)而不是擴充套件已有的索引(A)。還有一種情況是將一個索引擴充套件為(A,ID),其中ID是主鍵,對於InnoDB來說主鍵列已經包含在二級索引中了,所以這也是冗餘的。 大多數情況下都不需要冗餘索引,應該儘量擴充套件已有的索引而不是建立新索引。但也有時候出於效能方面的考慮需要冗餘索引,因為擴充套件已有的索引會導致其變得太大,從而影響其他使用該索引的查詢的效能。 例如,如果在整數列上有一個索引,現在需要額外增加一個很長的VARCHAR列來擴充套件該索引,那效能可能會急劇下降。特別是有查詢把這個索引當做覆蓋索引,或者這是MyISAM表並且有很多範圍查詢(由於MyISAM的字首壓縮)的時候。 有一個userinfo表。這個表有1000000行,對每個state_id值大概有20000條記錄。在state_id列有一個索引對下面的查詢有用,假設查詢名為Q1:

SELECT count(*) FROM userinfo WHERE state_Id=5;

一個簡單的測試表明該查詢的執行速度大概是每秒115次(QPS)。還有一個相關查詢需要檢索幾個列的值,而不是隻統計行數,假設名為Q2:

SELECT state_id,city,address FROM userinfo WHERE state_id=5;

對於這個查詢,測試結果QPS小於10。提升該查詢效能的最簡單辦法就是擴充套件索引為(state_id,city,address),讓索引能覆蓋查詢:

ALTER TABLE userinfo DROP KEY state_id, ADD
KEY state_id_2(state_id,city,address);

索引擴充套件後,Q2執行得更快了,但是Q1卻變慢了。如果我們想讓兩個查詢都變得更快,就需要兩個索引,儘管這樣一來原來的單列索引是冗餘的了。圖1顯示這兩個查詢在不同索引策略下的詳細結果,分別使用MyISAM和InnoDB儲存引擎。注意到只有state_id_2索引時,InnoDB引擎上的查詢Q1的效能下降並不明顯,這是因為InnoDB沒有使用索引壓縮。 圖1 有兩個索引的缺點是索引成本更高。圖2顯示了想表中插入100萬行資料所需要的時間。 圖2 可以看到,表中的索引越多插入速度越慢。一般來說,增加新索引將會導致INSERT、UPDATE、DELETE等操作的速度變慢,特別是當新增索引後導致達到了記憶體瓶頸的時候。 解決冗餘索引和重複索引的方法很簡單,刪除這些索引就可以,但首先要做的是找出這樣的索引。可以通過寫一些複雜的訪問INFORMATION_SCHEMA表的查詢來找,不過還有兩個更簡單的方法。可使用Shlomi Noach的common_schema中的一些檢視來定位,common_schema是一系列可以安裝到伺服器上的常用的儲存和檢視。這筆自己編寫查詢要快而且簡單。另外也可以使用Percona Toolkit中的pt-duplicate-key-checker,該工具通過分析表結構來找出冗餘和重複的索引。對於大型伺服器來說,使用外部的工具可能更合適些;如果伺服器上有大量的資料或者大量的表,查詢INFORMATION_SCHEMA表可能會導致效能問題。 在決定哪些索引可以被刪除的時候要非常小心。回憶一下,在前面的InnoDB的示例表中,因為二級索引的葉子節點包含了主鍵值,所以在列(A)上的索引就相當於在(A,ID)上的索引。如果有像WHERE A = 5 ORDER BY ID這樣的查詢,這個索引會很有作用。但如果將索引擴充套件為(A,B),則實際上就變成了(A,B,ID),那麼上面查詢的ORDER BY子句就無法使用該索引做排序,而只能用檔案排序了。所以,建議使用Percona工具箱中的pt-upgrade工具來仔細檢查計劃中的索引變更。