1. 程式人生 > >【七】高效能MySql筆記——高效能索引策略

【七】高效能MySql筆記——高效能索引策略

    正確的索引策略對效能優化相當重要。似乎沒有什麼事情是完美的,好的索引可以提高效能,壞的索引也可以拉低效能。所以在選擇合適的索引策略應該具有通觀全域性的思維。我們應該充分了解每種索引策略,以使我們可以靈活運用、靈活選擇。

    獨立的列

    在MySql查詢語句中條件不是獨立的列,則不會使用索引。即列不能是表示式的一部分,也不能是函式的引數。我們應該遵循始終將索引列放在表示式的一側。

    字首索引和索引選擇性

    字首索引就是對一列的前面一部分字串構建索引。索引很長的字元列會使效能變差。經過統計,往往我們只需要索引開始部分的列就可以滿足需求,同時也可以提高效率。不過這種方式會降低索引的選擇性。索引的選擇性是指不重複的索引值和資料表的記錄總數的比值。索引選擇性越高效率越高。MySql不允許索引BLOB、TEXT和很長的VARCHAR型別的列,所以對於這類列我們必須使用字首索引。為了找出最優的索引選擇性所對應的索引字串長度這裡有一個訣竅,首先字首應該足夠長;其次找出最常見的值的列表和最常見的字首列表進行比較;最後通過不斷的增加字首數量逐步逼近完整列的選擇性即為字首長度。對於資料量較大的表,我們可以通過逐步測試的方式選擇合適的字首數值。當發現再增加字首數值已經不能提高選擇性時即出現字首最佳值。字首索引的缺點:MySql不能使用字首索引進行ORDER BY和GROUP BY,也無法使用字首索引做覆蓋掃描。字尾索引是與字首索引相反的索引型別,可以通過將字首索引倒序儲存獲得。

    多列索引

    我們在建立索引時應該集中精力優化索引列的順序,或者建立一個全覆蓋索引。而不應該為資料表中的每一列都建立單獨的索引。雖然在新版本的MySql中可以通過“索引合併”策略,在一定程度上使用多個單列索引定位行。但這並不是一個好的方法,只是用於資料庫優化門外漢使用。我們可以使用optimizer switch來關閉索引合併功能。或者使用IGNORE INDEX提示讓優化器忽略掉某些索引。

    下面是識別不合適的多列索引的方法

        ①查詢服務對多個索引有相交操作時;

        ②查詢服務對多個索引進行聯合操作時(有多個OR條件);

        ③不合適的多列索引會誤導優化器,優化器會對查詢成本低估,同時會影響併發性。

    選擇合適的索引列順序

    選擇合適的索引列順序可以覆蓋更多的查詢語句,也會更好的滿足排序和分組的需要。索引列順序是從最左側開始的。優化時應首先考慮順序、I/O和排序,再使用將選擇性最高的列放最左列的方法。效能除了和索引列的選擇性有關,也和查詢條件的具體值有關(值得分佈)。我們需要根據執行頻率最高的查詢來調整索引列的順序。將資料型別總量更小的值列放在最左列可以快速縮小查詢範圍,提升查詢效率。這種方法的缺點是可能會使其他的查詢請求效能變差。即時我們選擇的比較合適的索引列順序也需要注意特殊情況下的查詢請求,往往這種查詢請求會摧毀整個應用的效能。在優化索引列順序提升效能時也需要注意WHERE子句中的排序、分組和範圍條件等其他因素,這些因素可能對查詢的效能造成非常大的影響。

    聚簇索引(Oracle索引組織表)

    聚簇的意思是資料行和相鄰的鍵值緊湊的儲存在一起。聚簇索引是一種資料儲存方式。由儲存引擎實現聚簇索引。每張表只能有一個聚簇索引。其在InnoDB中的實現是儲存了B-Tree索引和資料行。當存在聚簇索引時,資料行實際上存放在索引的葉子頁中。原理:InnoDB會選擇主鍵,沒有主鍵選擇唯一的非空索引,沒有唯一的非空索引隱式定義一個主鍵進行聚集資料。InnoDB只聚集同一個頁面中的資料,所以相鄰的鍵值可能會相距很遠(出於不同的頁面)。聚簇主鍵並非總是對效能有益。

    優點

        ①把資料儲存在一起;

        ②資料訪問更快;

        ③覆蓋索引掃描的值可以直接使用頁節點中的資料。

    缺點

        ①聚簇索引對記憶體有更高要求;

        ②插入速度嚴重依賴插入順序(按照主鍵的順序是插入最快的方式,非主鍵方式插入完成之後最好使用OPTIMIZE TABLE命令重新組織表);

        ③聚簇索引更新代價大;

        ④插入行或更新主鍵可能會面臨“頁分裂”,從而佔用更多的磁碟空間;

        ⑤聚簇索引會導致全表掃描變慢,尤其是行比較稀疏,或者頁分裂導致資料不連續的時候;

        ⑥二級索引(非聚簇索引)因為葉子節點包含了引用行的主鍵列,可能比想象的要更大;

        ⑦二級索引訪問需要兩次索引查詢(因為葉子節點儲存的是行的主鍵值)。

    注意:聚簇索引中的葉子節點儲存的是行的主鍵值,目的是為了減少當出現行移動或者資料頁分裂時二級索引的維護工作。為了更好的使用聚簇索引,我們建議即使用不到也為表增加一個自增的主鍵列,這樣可以保證資料行是按順序寫入,對於根據主鍵做關聯操作的效能也會更好。使用UUID作為聚簇索引會很糟糕,因為會使聚簇索引的插入完全隨機。在高併發的情況下,順序索引可能會造成明顯的主鍵爭用,爭用熱點發生在主鍵間隙的上界,可能導致間隙鎖競爭。間隙鎖定義可參考之前的文章,或自行Google。

    覆蓋索引

    設計優秀的索引應該考慮整個查詢。覆蓋索引是索引中包含所有需要查詢的欄位的值。MySql只能使用B-Tree索引做覆蓋索引。MySql不能在索引中執行LIKE操作,所以有LIKE操作的查詢語句不可以使用覆蓋索引。

    好處:

        ①索引條目通常遠小於資料行大小,減少資料訪問量;

        ②對於I/O密集型的範圍查詢有較好的效能提升;

        ③由於InnoDB的聚簇索引,覆蓋索引效果更好。


(注:type的index和Extra的Using index完全不通,type值只表示MySql執行查詢的方式)

    索引排序

    MySql實現排序的方法有:排序操作和索引順序掃描。MySql可以使用同一個索引既滿足排序,又用於查詢行。如果要進行倒序,則需要自行維護倒序列表資料。ORDER BY也需要滿足最左字首要求(如果WHERE或JOIN子句中左側為常量則也可以使用索引)。

    不能使用索引排序的情況

        ①兩種不同的排序方向(即DESC和ASC同時存在);

        ②ORDER BY 引用了不在索引中的列;

        ③WHERE和ORDER BY 的列無法組成索引最左字首;

        ④在索引的第一列上是範圍條件;

        ⑤對排序多個等於條件也是一種範圍查詢。

    壓縮(字首壓縮)索引

    MyISAM引擎使用字首壓縮來縮小索引的大小。預設只壓縮字串,通過引數設定也可以對整數做壓縮。壓縮塊使用更少的空間,代價是某些操作可能更慢。索引查詢必須從頭掃描,倒序掃描(ORDER BY DESC)效能會很差。對於CPU密集應用,壓縮索引會慢很多。好處是對於I/O密集型應用效果可能會好很多。通過在CREATE TABLE 中指定PACK KEYS引數來控制索引壓縮方式。

    冗餘和重複索引

    重複索引是指在相同的列上按照相同的順序建立的相同型別的索引。需要注意MySql的唯一限制和主鍵都是通過索引實現的。如果索引中有一個組合索引包含了一個單列索引,那麼這個單列索引就是重複索引(這裡同樣參考最左前列法則)。冗餘索引通常發生在新增索引中,如新增(A,B)索引,而原本就有一個(A)索引。或者新增(A,ID)索引,ID為主鍵,主鍵儲存在二級索引中,所以也是冗餘索引。單純的統計儘量使用MySql函式COUNT(),這樣可以不訪問原始資料提高效能。如果需要儘可能使用組合索引,而不是使用兩個或多個獨立索引,組合索引效能更優。檢查重複索引:通過訪問INFORMATION_SCHEMA表的查詢。解決:刪除冗餘和重複索引。也可通過common_schema中的一些檢視來定位。其中common_schema是一系列安裝到伺服器上的常用儲存和檢視。

    未使用的索引

    經常有些朋友在對資料庫索引不瞭解的情況下為了優化一些查詢建立的一些索引卻並沒有真正用到,這些索引不僅對優化沒有作用,而且有時還會使得我們的其他查詢效率減慢,因此這類未使用的索引是需要刪除的。我們可以通過Percona Server或者MariaDB的userstates伺服器變數進行一些監控,首先我們需要開啟這個變數(預設是關閉的),然後讓伺服器正常執行一段時間,再通過查詢INFORMATION_SCHEMA.INDEX_STATISTICS就可以查到每個索引的使用頻率。我們也可以使用Percona Toolkit中的pt-index-usage工具查詢日誌,並對日誌中的每條查詢進行EXPLAIN操作,然後打印出關於索引和查詢的報告、

    索引和鎖

    資料庫有表級和行級的鎖,每種級別的鎖都會給系統帶來額外的開銷,同時也會減少併發性。索引可以讓查詢鎖定更少的行。這裡我們只對最常用的InnoDB做一個提示:InnoDB在二級索引上使用共享鎖,但訪問主鍵索引需要排他鎖。所以就不能使用覆蓋索引了(覆蓋索引包含了需要查詢的值,因此排他鎖就不可以使用覆蓋索引),並且使得SELECT FOR UPDATE比LOCK IN SHARE MODE或非鎖定查詢要慢的多。