mysql筆記02 建立高性能的索引

建立高性能的索引mysql

1. 索引(在MySQL中也叫作"鍵(key)")是存儲引擎用於快速找到記錄的一種數據結構。算法

2. 索引能夠包含一個或多個列的值。若是索引包含多個列,那麼列的順序也十分重要,由於MySQL只能高效低使用索引的最左前綴列。sql

3. B-Tree索引:當人們談論索引的時候,若是沒有特別執行索引類型,那多半說是B-Tree索引,它使用B-Tree數據結構來存儲數據。緩存

    1). 可使用B-Tree索引的查詢類型:服務器

         a. 全值匹配:全值匹配指的是和索引中的全部列進行匹配。數據結構

         b. 匹配最左前綴:即只使用索引的第一列。併發

         c. 匹配列前綴:也能夠值匹配某一列的值的開頭部分。函數

         d. 匹配範圍值高併發

         e. 精確匹配某一列並範圍匹配另一列工具

         f. 只訪問索引的查詢:覆蓋索引

    2). 由於索引樹中的節點是有序的,因此除了按值查找以外,索引還能夠用於查詢中的ORDER BY操做(按順序查找)。通常來講,若是B-Tree能夠按照某種方式查找到值,那麼也能夠按照這種方式用於排序。

    3). B-Tree索引的限制

          a. 若是不是按索引的最左列開始查找,則沒法使用索引。

          b. 不能跳過索引中的列。

          c. 若是查詢中有某個列的範圍查詢,則最右邊的全部列都沒法使用索引優化查詢。若是範圍查詢列值的數量有限,那麼能夠經過使用多個等於條件來代替範圍查找。

4. 哈希索引(hash index):哈希索引基於哈希表實現,只有精確匹配索引全部列的查詢纔有效。結構十分緊湊,查詢速度很是快。

    InnoDB引擎有一個特殊的功能叫作"自適應哈希索引"。當InnoDB注意到某個索引值被使用得很是頻繁時,它會在內存中基於B-Tree索引之上再建立一個哈希索引,這樣B-Tree索引也就有哈希索引的一些優勢。

5. 空間數據索引,全文索引,其餘索引類別

6. 索引的優勢:索引可讓服務器快速定位到表的指定位置。最多見的B-Tree索引,按照順序存儲數據,因此MySQL能夠用來作ORDER BY 和 GROUP BY操做。總結下來,索引有以下三個優勢:

    1). 索引大大減小了服務器須要掃描的數據量

    2). 索引能夠幫助服務器避免排序和臨時表

    3). 索引能夠將隨機IO變爲順序IO。

7. 三星系統:索引將相關的記錄放到一塊兒則得到一星;若是索引中數據順序和查找中的排序順序一致則得到二星;若是索引中的列包含了查詢中須要的所有列則得到三星。

8. 索引是最好的解決方案嗎?對於很是小的表,大部分狀況下簡單的全表掃描更高效。對於中到大型的表,索引就很是有效。但對於特大型的表,創建和使用索引的代價將隨之增加。這種狀況下,則須要一種技術

    能夠直接區分出查詢須要的一組數據,而不是一條記錄一條記錄的匹配。

    若是表的數量特別多,能夠創建一個元數據信息表,用來查詢須要用到的某些特性。例如:記錄"哪一個用戶信息存儲在哪一個表裏面"。

9. 高性能的索引策略:

    1). 獨立的列:獨立的列是指索引列不能是表達式的一部分,也不能是函數的參數。若是查詢中的列不是獨立的,則MySQL就不會使用索引。

    2). 前綴索引和索引選擇性:有時候須要索引很長的字符列,這會讓索引變得很大且慢。一般能夠索引開始部分的字符,這樣能夠大大節約索引空間,從而提升索引效率。但這樣會下降索引的選擇性。

         a. 索引的選擇性是指,不重複的索引值(也稱爲基數)和數據表的記錄總數(#T)的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,由於選擇性高的索引可讓MySQL在查找時過濾掉更多的行。

             惟一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。

         b. 通常狀況下某個列前綴的選擇性也是足夠高的,足以知足查詢性能。對於BLOB、TEXT或者很長的VARCHAR類型的列,必須使用前綴索引,由於MySQL不容許索引這些列的完整長度。

         c. 訣竅在於要選擇足夠長的前綴以保證較高的選擇性,同時又不能太長(以便節省空間)。計算合適的前綴長度的一個方法是計算完整列的選擇性,並使前綴的選擇性接近於完整列的選擇性。

            下面是如何計算完整列的選擇性:SELECT COUNT(DISTINCT city)/COUNT(*) FROM sakila.city_demo;        查詢結構值爲:0.031

            一般來講,這個例子中若是前綴的選擇性可以接近於0.031,基本上就能夠用了。能夠在一個查詢中針對不一樣前綴長度進行計算,這對於大表很是有用。

            下面給出瞭如何在同一個查詢中計算不一樣前綴長度的選擇性:

             SELECT COUNT(DISTINCT LEFT(city,3)) AS sel3,COUNT(DISTINCT LEFT(city,4)) AS sel4,COUNT(DISTINCT LEFT(city,5)) AS sel5,

                         COUNT(DISTINCT LEFT(city,6)) AS sel6,COUNT(DISTINCT LEFT(city,7)) AS sel7 FROM sakila.city_demo;

             查詢結果值,按順序爲:0.0238 ,  0.0293, 0.0305,0.0309,0.0310

             查詢顯示當前前綴長度到達7的時候,再增長前綴長度,選擇性提高的幅度已經很小了。

         d. 只看平均選擇性是不夠的,也有例外的狀況,須要考慮最壞狀況下的選擇性。

         e. 前綴索引是一種能使索引更小、更快的有效辦法,但另外一方面也有其缺點:MySQL沒法使用前綴索引作ORDER BY和GROUP BY , 也沒法使用前綴索引作覆蓋掃描。

         f. 有時候後綴索引也有用途(例如,找到某個域名的全部電子郵件地址)。MySQL原生並不支持反向索引,可是能夠把字符串反轉後存儲,並基於此創建前綴索引。能夠經過觸發器來維護這種索引。

    3). 多列索引:在MySQL或更新的版本中,會使用"索引合併"策略,查詢能同時使用兩個單列索引進行掃描,並將結果進行合併。這種算法有三個變種:OR條件的聯合(union),AND條件的相交,

         組合前兩種狀況的聯合及相交。

         索引合併策略有時候是一種優化的結果,但實際上更多的時候說明表上的索引建的很糟糕:

         a. 當出現服務器對多個索引作相交操做時(一般多個AND條件),一般意味着須要一個包含全部相關列的多列索引,而不是多個獨立的單列索引。

         b. 當服務器須要對多個索引作聯合操做時(一般由多個OR條件),一般須要耗費大量CPU和內存資源在算法的緩存、排序和合並操做上。特別是當其中有些索引的選擇性不高,須要合併掃描返回的大量數據時。

         c. 優化器不會把這些計算到"查詢成本"(cost)中,優化器值關係隨機頁面讀取。這樣不只消耗更多的CPU和內存資源,還會影響查詢的併發性。

         d. 若是在EXPLAIN中看到有索引合併,應該好好檢查一下查詢和表的結構,看是否是已經最優的。也能夠哦太難過參數optimizer_switch來關閉索引合併功能。也可使用IGNORE INDEX提示讓優化器

             忽略掉某些索引。

    4). 選擇合適的索引列順序:正確的索引順序依賴於使用該索引的查詢,並同時知足須要考慮如何更好地知足排序和分組的須要。

         a. 在一個多列的B-Tree索引中,索引列的順序意味着索引首先按照最左列進行排序,其次是第二列,等等。因此,索引能夠按照升序或者降序進行全表掃描,以知足符合列順序的ORDER BY,GROUP BY和

             DISTINCT等字句的查詢需求。

         b. 當不須要考慮排序和分組時,將選擇性最高的列放在前面一般是很好的。這時候索引的做用只是用於優化WHERE條件查詢。

         c. 更具運行頻率最高的查詢來調整索引的順序,讓這種狀況下索引的選擇性最高。

         d. 若是是從諸如pt-query-digest這樣的工具的報告中提起"最差"查詢,那麼再按上面辦法選定索引順序每每是很是高效的。若是沒有相似的具體查詢來運行,那麼最好仍是按經驗法則來作,由於全局法則考

             濾的是全局基數和選擇性,而不是某個具體查詢。

             SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity, count(*) from payment

             查詢結果值爲:staff_id_selectivity  0.0001,customer_id_selectivity  0.0373, count(*)  16049 

             customer_id 的選擇性更高,因此答案是將其做爲索引列的第一列

         e. 儘管關於選擇性和基數的經驗法則值得去研究和分析,但必定要記住別忘了WHERE字句中的排序、分組和範圍條件等其餘因素,這些因素可能對查詢的性能形成很是大的影響。

    5). 聚簇索引:InnoDB的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行。它的數據實際上存儲在索引的葉子頁中。"聚簇"表示把數據行和相鄰的鍵值緊湊地存儲在一塊兒。由於沒法同時把數據行存放在

          兩個不一樣的地方,因此一個表只能有一個聚簇索引。

         5.1). InnoDB將經過主鍵彙集數據。若是沒有定義主鍵,InnoDB會選擇一個惟一的非空索引代替。若是沒有這樣的索引,InnoDB會隱式定義一個主鍵做爲聚簇索引。InnoDB只彙集在同一個頁面中的記錄。包

                  含相鄰鍵值的頁面可能會相距甚遠。

         5.2). 聚簇索引的優勢:

                 a. 能夠把相關的數據保存在一塊兒。

                 b. 數據訪問更快。

                 c. 使用覆蓋索引掃描的查詢能夠直接使用葉節點中的主鍵值。

         5.3). 聚簇索引的缺點:

                 a. 更新聚簇索引列的代價很高,由於會強制將每一個被更新的行移動到新的位置。

                 b. 可能會致使頁分裂。

                 c. 致使全表掃描變慢,尤爲是行比較稀疏。

                 e. 二級索引(非聚簇索引)可能比想象的要更大,由於在二級索引的葉子節點包含了引用行的主鍵列。

                 f. 二級索引訪問須要兩次索引查詢,而不是一次。這是由於二級索引葉子節點保存的不是指向行的物理位置的指針,而是行的主鍵值。這意味着經過二級索引查找行,存儲引擎須要找到二級索引的葉子

                    節點得到對應的主鍵值,而後根據這個值去聚簇索引中查找到對應的行。這裏作了重複工做:兩次B-Tree查找而不是一次。

         5.4). 最好避免隨機的(不連續且值的分佈範圍很是大)聚簇索引,特別是對於IO密集型的應用。例如:從性能角度考慮,使用UUID做爲聚簇索引會很糟糕:它使得聚簇索引的插入變得徹底隨機,這是最壞的狀況,

                  是的數據沒有任何彙集。向UUID主鍵插入行不只花費更長的時間,並且索引佔用的空間也更大。這一方面是因爲主鍵字段更長,另外一方面毫無疑問是因爲頁分裂碎片致使的。

         5.5). 使用InnoDB時應該儘量地按主鍵順序插入數據,而且儘量地使用單調增長的聚簇鍵的值插入新行。這樣能夠順序地寫入數據,減小隨機IO,減小碎片和減小分頁。

                 但對於高併發工做負載,按主鍵順序插入可能形成明顯的爭用。

     6). 覆蓋索引:若是一個索引包含(或者說覆蓋)全部須要查詢的字段的值,咱們就稱之爲"覆蓋索引"

          6.1 覆蓋索引的好處:

                a. 索引條目一般遠小於數據行大小,因此若是隻須要讀取索引,那麼MySQL就會極大地減小數據訪問量。

                b. 由於索引是按照值順序存儲的(至少在單個頁內如此),因此對於IO密集型的範圍查詢會比隨機從磁盤讀取每一行數據的IO要少得多。

                c. 因爲InnoDB的聚簇索引,覆蓋索引對InnoDB表特別有用。InnoDB的二級索引在葉子節點中保存了行的主鍵值,因此若是二級主鍵可以覆蓋查詢,則能夠避免對主鍵索引的二次查詢。

          6.2 不是全部類型的索引均可以做爲覆蓋索引。覆蓋索引必需要存儲索引列的值,而哈希索引、空間索引和全文索引等都不存儲索引列的值,因此MySQL只能使用B-Tree索引作覆蓋索引。不是全部

                的索引都支持覆蓋索引。

          6.3 當發起一個索引覆蓋的查詢時,在EXPLAIN的Extra列能夠看到"Using index"的信息。

          6.4 MySQL不能在索引中執行LIKE操做。由於該操做可能轉換爲簡單的比較操做,可是若是是通配符開頭的LIKE查詢,存儲引擎就沒法比較匹配。MySQL服務器只能提取數據行的值而不是索引值來做比較。

          6.5 延遲關聯:延遲對列的訪問。在查詢第一階段MySQL能夠作覆蓋索引,在FROM子句的子查詢中使用索引。

     7). 使用索引掃描來作排序:MySQL有兩種方式能夠生成有序的結果:經過排序操做;或者按索引順序掃描;若是EXPLAIN出來的type列的值爲"index",則說明MySQL使用了索引來作排序(不要和Extra列的

          "Using index"搞混淆了)。

           a. 索引自己是很快的,由於只須要從一條索引記錄移動到緊接着的下一條記錄。若是索引不能覆蓋查詢所需的所有列,那就不得不每掃描一條索引記錄就回表查詢一次對應的行。這基本上都是隨機IO,所以

               按索引順序讀取數據的速度一般要比順序地全表掃描慢,尤爲是在IO密集型的工做負載時。

           b. 只有當索引的列順序和ORDER BY子句順序徹底一致,而且全部列的順序方向(倒序或正序)都同樣時,MySQL纔可以使用索引來對結果作排序。若是查詢須要關聯多張表,則只有ORDER BY子句引用的

               字段所有爲第一個表時,才能使用索引作排序。

           c. 有一種狀況下ORDER BY子句能夠不知足索引的最左前綴的要求,就是前導列爲常量的時候。

      8). 壓縮(前綴索引):MyISAM使用前綴索引壓縮來減小索引的大小,從而讓更多的索引能夠放入內存中,這在某些狀況下能極大地提升性能。默認值壓縮字符串,到哪經過參數設置能夠對整數壓縮。

      9). 冗餘和重複索引:MySQL容許在相同列上建立多個索引,不管是有意仍是無心的。MySQL須要單獨維護重複的索引,而且優化器在優化查詢的是時候也須要逐個地進行考慮,這會影響性能。

           a. 重複索引:

              例如以下代碼:CREATE TABLE test (ID INT NOT NULL PRIMARY KEY, A INT NOT NULL,B INT NOT NULL,UNIQUE(ID),INDEX(ID)) ENGINE = InnoDB

              一個經驗不足的用戶多是想建立一個主鍵,先加上惟一限制,而後再加上索引以供查詢使用。事實上,MySQL的惟一限制和主鍵限制都是經過索引實現的,所以,上面的寫法實際上在相同的列上建立了三個

              重複的索引。一般並無理由這樣作,除非是在同一列上建立不一樣類型的索引來知足不一樣的查詢需求。

           b. 冗餘索引:

               若是建立了索引(A,B),在建立索引(A)就是冗餘索引,由於這只是前一個索引的前綴索引。

               冗餘索引一般發生在爲表添加新索引的時候。例如,有人可能會增長一個新的索引(A,B)而不是擴展已有的索引(A)。還有一種狀況是將一個索引擴展爲(A,ID),其中ID是主鍵,對於InnoDB來講主鍵已經包含

               在二級索引中了,因此這也是冗餘的。

           c. 解決冗餘索引和重複索引的方法很簡單,刪除這些索引就能夠了,但首先要找出這樣的索引。

      10). 未使用的索引:對於服務器上一些永遠不用的索引,徹底是累贅,建議考慮刪除。

      11). 索引和鎖:

             a. InnoDB只有在訪問行的時候纔會對其進行加鎖,而索引可以減小InnoDB訪問的行數,從而減小鎖的數量。

10. 索引案例學習:

      1). 過濾條件:

           a. 能夠經過使用IN語句讓MySQL使用索引,避免使用範圍查詢致使SQL語句沒法使用索引。可是這種技巧也不能濫用,由於每額外增長一個IN()條件,優化器就須要作的組合(每一個in的一個元素

               至關於一條查詢)都將以指數形式增長。

           b. 在有更多不一樣值的列上建立索引的選擇性會更好。通常來講這樣作都是對的,由於可讓MySQL更有效的過濾掉不須要的行。

           c. 儘量將須要作範圍查詢的列放到索引的後面,以便優化器能使用盡量多的索引列。

           d. 考慮表上的全部選項。當設計索引時,不要只爲現有的查詢考慮須要哪些索引,還須要考慮對查詢進行優化。

      2). 避免多個範圍條件:

           a. MySQL沒法再使用範圍列後面的其餘索引列了,可是對於"多個等值條件查詢"則沒有這個限制。

      3). 優化排序:

           a. 對於那些選擇性很是低的列,能夠增長一些特殊的索引來鎖排序。例如:能夠建立(sex,rating)索引:SELECT <cols> FROM profiles WHERE sex='M' order by rating LIMIT 10;

               這個查詢同時使用了ORDER BY 和 LIMIT ,若是沒有索引的話會很慢。

           b. 優化索引另外一個比較好的策略是使用延遲關聯,經過使用覆蓋索引查詢返回須要的主鍵,再根據這些主鍵關聯原表得到須要的行。這能夠減小MySQL掃描那些須要丟棄的行數。

11. 維護索引和表:

      1). 找到並修復損壞的表:若是遇到數據損壞,最重要的是找出是什麼致使了損壞,而不僅是簡單地修復,不然可能還會不斷的損壞。能夠經過設置innodb_force_recovery參數進入InnoDB的強制回覆模式來

            修復數據。另外還能夠經過使用開源的InnoDB數據恢復工具箱直接從InnoDB數據文件恢復數據。

      2). 更新索引統計信息:InnoDB不在磁盤存儲索引統計信息,而是經過隨機的索引訪問進行評估並將其存儲在內存中。InnoDB引擎經過抽樣的方式計算統計信息,首先隨機地讀取少許的索引頁面,而後以此

           爲樣本計算索引的統計信息。

      3). 減小索引和數據的碎片:

            數據碎片分類:

            行碎片:這種碎片指的是數據行被存儲爲多個地方的多個片斷中。即便查詢值從索引中訪問一行記錄,行碎片也會致使性能降低。

            行間碎片:行間碎片是指邏輯上順序的夜,或者行在磁盤上不是順序存儲的。行間碎片對諸如全表掃描和聚簇索引掃描之類的操做有很大的影響,由於這些操做本來能從磁盤上順序存儲的數據中獲益。

            剩餘空間碎片:剩餘空間碎片是指數據頁中有大量的空餘空間。這會致使服務器讀取大量不須要的數據,從而形成浪費。

            InnoDB不會出現短小的行碎片,InnoDB會移動短小的行並重寫到一個片斷中。

            新版的InnoDB新增了"在線添加"和刪除索引功能,能夠先刪除,而後再從新建立索引的方式來消除索引的碎片化。

12. mysql中每一個表都有一個聚簇索引(clustered index),除此以外的表上的每一個非聚簇索引都是二級索引,又叫輔助索引(secondary indexes)。

 

 

索引的優化:

1. 選擇合適的索引列順序

2. 若是查詢中有某個列的範圍查詢,則最右邊的全部列都沒法使用索引優化查詢。若是範圍查詢列值的數量有限,那麼能夠經過使用多個等於條件來代替範圍查找。

3. 前綴索引

4. 使用索引掃描來作排序

5. 覆蓋索引

 

 

6. 索引的優勢:索引可讓服務器快速定位到表的指定位置。最多見的B-Tree索引,按照順序存儲數據,因此MySQL能夠用來作ORDER BY 和 GROUP BY操做。總結下來,索引有以下三個優勢:

    1). 索引大大減小了服務器須要掃描的數據量

    2). 索引能夠幫助服務器避免排序和臨時表

    3). 索引能夠將隨機IO變爲順序IO。

相關文章
相關標籤/搜索