Mysql索引優化分析-第一篇


1.性能下降SQL慢 執行時間長 等待時間長

查詢語句寫的爛

索引失效(單值,復合)

關聯查詢太多join(設計缺陷或不得已的需求)

服務器調優及各個參數設置(緩沖\線程數等)

2.常見通用的join查詢

2.1SQL執行順序

2.1.1手寫

在這里插入圖片描述

2.1.2機讀

在這里插入圖片描述

2.1.3總結

在這里插入圖片描述

2.2Join圖

在這里插入圖片描述

2.3建表SQL

2.4 7種Join

3.索引簡介

3.1什么是索引

MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數據的數據結構。
可以得到索引的本質:索引是數據結構

可以簡單理解為"排好序的快速查找數據結構"。

詳解(重要):

在這里插入圖片描述
結論:

數據本身之外,數據庫還維護着一個滿足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就可以在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引。

一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以文件形式存儲在硬盤上.

我們平時所說的索引,如果沒有特別指明,都是指B樹(多路搜索樹,並不一定是二叉樹)結構組織的索引。其中聚集索引,次要索引,覆蓋索引,復合索引,前綴索引,唯一索引默認都是使用B+樹索引,統稱索引。當然,除了B+樹這種類型的索引之外,還有哈希索引(hash index)等。

3.2索引優勢

  • 類似大學圖書館建書目索引,提高數據檢索效率,降低數據庫的IO成本

  • 通過索引列對數據進行排序,降低數據排序成本,降低了CPU的消耗

3.3索引劣勢

  • 實際上索引也是一張表,該表保存了主鍵和索引字段,並指向實體表的記錄,所以索引列也是要占用空間的
  • 雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如果對表INSERT,UPDATE和DELETE。因為更新表時,MySQL不僅要不存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因為更新所帶來的鍵值變化后的索引信息.
  • 索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立優秀的索引,或優化查詢語句

3.4MySQL分類

3.4.1單值索引

即一個索引只包含單個列,一個表可以有多個單列索引。

建議一張表索引不要超過5個;

優先考慮復合索引

3.4.2唯一索引

索引列的值必須唯一,但允許有空值

3.4.3復合索引

即一個索引包含多個列

3.4.4基本用法

  • 創建

CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));

如果是CHAR,VARCHAR類型,length可以小於字段實際長度;
如果是BLOB和TEXT類型,必須指定length。

ALTER mytable ADD [UNIQUE] INDEX [indexName] ON(columnname(length));

  • 刪除

DROP INDEX [indexName] ON mytable;

  • 查看

SHOW INDEX FROM table_name\G

  • 使用ALTER命令

在這里插入圖片描述

3.5MySQL索引架構

3.5.1BTree索引

在這里插入圖片描述

在這里插入圖片描述

Btree索引(或Balanced Tree),是一種很普遍的數據庫索引結構,oracle默認的索引類型(本文也主要依據oracle來講)。其特點是定位高效、利用率高、自我平衡,特別適用於高基數字段,定位單條或小范圍數據非常高效。理論上,使用Btree在億條數據與100條數據中定位記錄的花銷相同。

數據結構利用率高、定位高效
Btree索引的數據結構如下:

在這里插入圖片描述

結構看起來Btree索引與Binary Tree相似,但在細節上有所不同,上圖中用不同顏色的標示出了Btree索引的幾個主要特點:

樹形結構:由根節(root)、分支(branches)、葉(leaves)三級節點組成,其中分支節點可以有多層。
多分支結構:與binary tree不相同的是,btree索引中單root/branch可以有多個子節點(超過2個)。
雙向鏈表:整個葉子節點部分是一個雙向鏈表(后面會描述這個設計的作用)
單個數據塊中包括多條索引記錄
這里先把幾個特點羅列出來,后面會說到各自的作用。

結構上Btree與Binary Tree的區別,在於binary中每節點代表一個數值,而balanced中root和Btree節點中記錄了多條”值范圍”條目(如:[60-70][70-80]),這些”值范圍”條目分別指向在其范圍內的葉子節點。既root與branch可以有多個分支,而不一定是兩個,對數據塊的利用率更高。

在Leaf節點中,同樣也是存放了多條索引記錄,這些記錄就是具體的索引列值,和與其對應的rowid。另外,在葉節點層上,所有的節點在組成了一個雙向鏈表。
了解基本結構后,下圖展示定位數值82的過程:

在這里插入圖片描述

演算如下:
讀取root節點,判斷82大於在0-120之間,走左邊分支。
讀取左邊branch節點,判斷82大於80且小於等於120,走右邊分支。
讀取右邊leaf節點,在該節點中找到數據82及對應的rowid
使用rowid去物理表中讀取記錄數據塊(如果是count或者只select rowid,則最后一次讀取不需要)

在整個索引定位過程中,數據塊的讀取只有3次。既三次I/O后定位到rowid。

而由於Btree索引對結構的利用率很高,定位高效。當1千萬條數據時,Btree索引也是三層結構(依稀記得億級數據才是3層與4層的分水嶺)。定位記錄仍只需要三次I/O,這便是開頭所說的,100條數據和1千萬條數據的定位,在btree索引中的花銷是一樣的。

平衡擴張
除了利用率高、定位高效外,Btree的另一個特點是能夠永遠保持平衡,這與它的擴張方式有關。(unbalanced和hotspot是兩類問題,之前我一直混在一起),先描述下Btree索引的擴張方式:

新建一個索引,索引上只會有一個leaf節點,取名為Node A,不斷的向這個leaf節點中插入數據后,直到這個節點滿,這個過程如下圖(綠色表示新建/空閑狀態,紅色表示節點沒有空余空間):

在這里插入圖片描述

當Node A滿之后,我們再向表中插入一條記錄,此時索引就需要做拆分處理:會新分配兩個數據塊NodeB & C,如果新插入的值,大於當前最大值,則將Node A中的值全部插入Node B中,將新插入的值放到Node C中;否則按照5-5比例,將已有數據分別插入到NodeB與C中。

無論采用哪種分割方式,之前的leaf節點A,將變成一個root節點,保存兩個范圍條目,指向B與C,結構如下圖(按第一種拆分形式):

在這里插入圖片描述

當Node C滿之后,此時 Node A仍有空余空間存放條目,所以不需要再拆分,而只是新分配一個數據塊Node D,將在Node A中創建指定到Node D的條目:

在這里插入圖片描述

如果當根節點Node A也滿了,則需要進一步拆分:新建Node E&F&G,將Node A中范圍條目拆分到E&F兩個節點中,並建立E&F到BCD節點的關聯,向Node G插入索引值。此時E&F為branch節點,G為leaf節點,A為Root節點:

在這里插入圖片描述

在整個擴張過程中,Btree自身總能保持平衡,Leaf節點的深度能一直保持一致。

實際應用中的一些問題
前面說完了Btree索引的結構與擴張邏輯,接下來講一些Btree索引在應用中的一些問題:

單一方向擴展引起的索引競爭(Index Contention)

若索引列使用sequence或者timestamp這類只增不減的數據類型。這種情況下Btree索引的增長方向總是不變的,不斷的向右邊擴展,因為新插入的值永遠是最大的。

當一個最大值插入到leaf block中后,leaf block要向上傳播,通知上層節點更新所對應的“值范圍”條目中的最大值,因此所有靠右邊的block(從leaf 到branch甚至root)都需要做更新操作,並且可能因為塊寫滿后執行塊拆分。

如果並發插入多個最大值,則最右邊索引數據塊的的更新與拆分都會存在爭搶,影響效率。在AWR報告中可以通過檢測enq: TX – index contention事件的時間來評估爭搶的影響。解決此類問題可以使用Reverse Index解決,不過會帶來新的問題。

Index Browning 索引枯萎(不知道該怎么翻譯這個名詞,就是指leaves節點”死”了,樹枯萎了)

其實oracle針對這個問題有優化機制,但優化的不徹底,所以還是要拿出來的說。

我們知道當表中的數據刪除后,索引上對應的索引值是不會刪除的,特別是在一性次刪除大批量數據后,會造成大量的dead leaf掛到索引樹上。考慮以下示例,如果表100以上的數據會部被刪除了,但這些記錄仍在索引中存在,此時若對該列取max()

在這里插入圖片描述
通過與之前相同演算,找到了索引樹上最大的數據塊,按照記錄最大的值應該在這里,但發現這數據塊里的數據已經被清空了,與是利用Btree索引的另一個特點:leaves節點是一個雙向列表,若數據沒有找到就去臨近的一個數據塊中看看,在這個數據塊中發現了最大值99。

在計算最大值的過程中,這次的定位多加載了一個數據塊,再極端的情況下,大批量的數據被刪除,就會造成大量訪問這些dead leaves。

針對這個問題的一般解決辦法是重建索引,但記住! 重建索引並不是最優方案,詳細原因可以看看這。使用coalesce語句來整理這些dead leaves到freelist中,就可以避免這些問題。理論上oracle中這步操作是可以自動完成的,但在實際中一次性大量刪除數據后,oracle在短時間內是反應不過來的。

3.5.2Hash索引

3.5.3full-text全文索引

3.5.4R-Tree索引

3.6那些情況需要創建索引

1.主鍵自動建立唯一索引

2.頻繁作為查詢的條件的字段應該創建索引

3.查詢中與其他表關聯的字段,外鍵關系建立索引

4.頻繁更新的字段不適合創建索引

因為每次更新不單單是更新了記錄還會更新索引,加重IO負擔

5.Where條件里用不到的字段不創建索引

6.單間/組合索引的選擇問題,who?(在高並發下傾向創建組合索引)

7.查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序的速度

8.查詢中統計或者分組字段

3.7哪些情況不要創建索引

1.表記錄太少

2.經常增刪改的表

3.數據重復且分布平均的表字段,因此應該只為經常查詢和經常排序的數據列建立索引。
注意,如果某個數據列包含許多重復的內容,為它建立索引就沒有太大的實際效果。

在這里插入圖片描述

4.性能分析

4.1MySQL Query Optimizer

在這里插入圖片描述

4.2MySQL常見瓶頸

  • CPU:CPU在飽和的時候一般發生在數據裝入在內存或從磁盤上讀取數據時候
  • IO:磁盤I/O瓶頸發生在裝入數據遠大於內存容量時
  • 服務器硬件的性能瓶頸:top,free,iostat和vmstat來查看系統的性能狀態

4.3Explain

4.3.1是什么(查看執行計划)

  • 使用EXPLAIN關鍵字可以模擬優化器執行SQL語句,從而知道MySQL是如何處理你的SQL語句的。分析你的查詢語句或是結構的性能瓶頸。

4.3.2能干嘛

  • 表的讀取順序
  • 數據讀取操作的操作類型
  • 哪些索引可以使用
  • 哪些索引被實際使用
  • 表之間的引用
  • 每張表有多少行被優化器查詢

4.3.3怎么用

  • Explain+SQL語句

  • 執行計划包含的信息

在這里插入圖片描述

4.3.4各個字段解釋

  • id

    select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序

    三種情況:

    id相同,執行順序由上至下

在這里插入圖片描述

id不同,如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行

在這里插入圖片描述

id相同不同,同時存在

在這里插入圖片描述

  • select_type

    • 有哪些

在這里插入圖片描述

  • 查詢的類型,主要用於區別普通查詢、聯合查詢、子查詢等的復雜查詢

1.SIMPLE

簡單的select查詢,查詢中不包含子查詢或者UNION

2.PRIMARY

查詢中若包含任何復雜的子部分,最外層查詢則被標記為

3.SUBQUERY

在SELECT或者WHERE列表中包含了子查詢

4.DERIVED、

在FROM列表中包含的子查詢被標記為DERIVED(衍生)MySQL會遞歸執行這些子查詢,把結果放在臨時表里。

5.UNION

若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED

6.UNION RESULT

從UNION表獲取結果的SELECT

  • table

顯示這一行的數據是關於哪張表的

  • type

在這里插入圖片描述

訪問類型排列

在這里插入圖片描述

顯示查詢使用了何種類型
從最好到最差依次是:
system>const>eq_ref>ref>range>index>ALL

system

表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,這個也可以忽略不計

const

表示通過索引一次就找到了,const用於比較primary key或者unique索引。因為只匹配一行數據,所以很快。如將主鍵至於where列表中,MySQL就能將該查詢轉換為一個常量

在這里插入圖片描述

eq_ref

唯一性索引,對於每個索引鍵,表中只有一條記錄與之匹配,常見於主鍵或唯一索引掃描

在這里插入圖片描述

ref

非唯一索引掃描,返回匹配某個單獨值的所有行。本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體

在這里插入圖片描述

range

只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引一般就是在你的where語句中出現了between、<、>、in等的查詢這種范圍掃描索引掃描比全表掃描要好,因為他只需要開始索引的某一點,而結束語另一點,不用掃描全部索引

index

Full Index Scan,index與ALL區別為index類型只遍歷索引樹。這通常比ALL快,因為索引文件通常比數據文件小。
也就是說雖然all和index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的
在這里插入圖片描述

all

FullTable Scan,將遍歷全表以找到匹配的行

備注:

一般來說,得保證查詢只是達到range級別,最好達到ref

  • possible_keys

顯示可能應用在這張表中的索引,一個或多個。
查詢涉及的字段上若存在索引,則該索引將被列出,但不一定被查詢實際使用

  • key

實際使用的索引。如果為null則沒有使用索引

查詢中若使用了覆蓋索引,則索引和查詢的select字段重疊

  • key_len

表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好

key_len顯示的值為索引最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的

  • ref

顯示索引那一列被使用了,如果可能的話,是一個常數。那些列或常量被用於查找索引列上的值

在這里插入圖片描述

  • rows

根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數,越少越好

在這里插入圖片描述

  • Extra

包含不適合在其他列中顯示但十分重要的額外信息

  • 1.Using filesort

說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成排序操作成為“文件排序”性能不好

在這里插入圖片描述

  • 2.Using temporary

使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by 和分組查詢 group by

在這里插入圖片描述

  • 3.USING index

表示相應的select操作中使用了覆蓋索引(Coveing Index),避免訪問了表的數據行,效率不錯!
如果同時出現using where,表明索引被用來執行索引鍵值的查找;如果沒有同時出現using where,表面索引用來讀取數據而非執行查找動作。

在這里插入圖片描述

覆蓋索引(Covering Index)

在這里插入圖片描述

  • 4.Using where

表面使用了where過濾

  • 5.using join buffer

使用了連接緩存

  • 6.impossible where

where子句的值總是false,不能用來獲取任何元組

在這里插入圖片描述

  • 7.select tables optimized away

在沒有GROUPBY子句的情況下,基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段再進行計算,查詢執行計划生成的階段即完成優化。

  • 8.distinct

優化distinct,在找到第一匹配的元組后即停止找同樣值的工作

4.3.5熱身案列

在這里插入圖片描述

在這里插入圖片描述

微信公眾號
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM