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熱身案列
微信公眾號