mysql效能調優筆記(二)--查詢優化和索引
一、Mysql執行查詢流程
mysql執行查詢的流程
mysql執行查詢內部路程:1.客服端傳送一條查詢給伺服器
2.伺服器先檢查查詢快取,如果命中快取,立刻返回儲存在快取的結果,否則進入下一階段。
3.伺服器端進行sql解析,預處理,再由優化器生成對應的執行計劃。
將一個SQL轉換成一個執行計劃,MySQL再依照這個執行計劃和儲存引擎進行互動,者包括多個子階段,解析SQL,預處理,優化sql執行計劃。
4.mysql根據優化器生成的執行計劃,呼叫儲存引擎的api來執行查詢
5.將結果返回給客戶端。
優化資料訪問: 1、只查詢需要的記錄,如使用limit等
2、多表關聯時不要返回全部列
3、查詢不取出全部列
4、不要重複查詢相同的資料:不斷重複執行相同的查詢,然後每次都返回相同的資料,比較好的方案是,當初次查詢的時候將這個資料快取起來,需要的時候從快取中取出。
對於MySQl,最簡單的衡量查詢開銷的三個指標如下:
響應時間
掃描的行數
返回的行數
語法解析器 : Mysql通過關鍵字將SQL語句進行解析,並生成一棵對應的“解析樹”。MySQL解析器將使用mysql語法規則驗證和解析查詢。 如:驗證是否使用錯誤的關鍵字,或者使用關鍵字的順序是否正確等。或者它還會驗證引號是否能前後正確匹配。
前處理器 :根據一些MySQL規則進一步檢查解析樹是否合法。如:檢查資料表和資料列是否存在,還會解析名字和別名,看看它們是否有歧義。
查詢優化器:使用基於成本的優化器,嘗試預測一個查詢使用某個執行計劃的成本,並選擇成本最小的一個。
使用很多優化策略來生成一個最優的執行計劃,優化策略可以簡單地分為,一種靜態優化,一種動態優化。
二、索引
對於非常小的表,大部分情況下簡單的全表掃描更高效。對於中到大型的表,索引就非常有效。
B-Tree索引
使用B-Tree資料結構來儲存資料,索引型別大多數指的是B-Tree索引。
除了Archive引擎不支援這種索引,其他大多數都支援。
B-Tree所有的值按照順序儲存,每一個葉子頁到根的 距離相同。
InnoDB索引工作流程
B-Tree索引能夠快速訪問資料, 因為儲存引擎不再需要 進行全表掃描來獲取需要的資料,而是從索引的根節點開始進行搜尋,根節點的槽中存放了指向子節點的指標,儲存引擎根據這些指標向下層查詢。通過比較節點頁的值和要查詢的值可以找到合適的指標進入下層子節點。最終儲存一起拿要麼找到對應值,要麼記錄不存在。
B-Tree索引組織資料儲存
B-Tree對索引列時順序組織儲存的,進行排序的依據是CREATE TABLE 語句中定義索引時列的順序。
看一下最後兩個條目,兩個人的姓和名都一樣,則根據他們的出生日期來排序。
B-Tree索引適用於全鍵值、鍵值範圍或鍵字首查詢。
其中鍵字首查詢只適用於根據最左字首的查詢。
前面的索引對以下型別的查詢有效:
全值匹配 : 是和索引中的所有列進行匹配,
匹配最左字首 : 用於查詢所有姓為Allen的人,即只使用索引的第一列。
匹配列字首: 只匹配某一列的值的開頭部分。如:可用於查詢所有以J開頭的姓的人。
匹配範圍值: 可用於查詢姓在Allen和Barraymore之間的人。
精確匹配某一列並範圍匹配另外一列: 可用於查詢所有姓為Allen,並且名字是字母K開頭(如:Kim,Karl等)的人,
即第一列last_name全匹配,第二列first_name範圍匹配。
只訪問索引的 查詢:查詢只需要訪問索引,不需要訪問資料行。
B-Tree索引限制:
1、索引不是最左列查詢,則無法使用,如:無法查詢某個特定生日的人,不是最左資料列。
如果不指定(first_name),就無法查詢姓為Smith並且在某個特定日期出生的人。 否則mysql只能使用索引的第一列。
2、如果查詢中有某個列的範圍查詢,則其右邊所有列都無法使用索引優化查詢。
例如:有查詢WHERE last_name='Smith' AND first_name LIKE 'J%' AND dob = '1976-12-23',這個查詢只能使用索引的前兩列,因為這個LIKE是一個範圍條件。
雜湊索引
雜湊索引基於雜湊表的實現,只有精確匹配索引所有列的查詢才有效。
雜湊索引將所有的雜湊碼儲存在索引中,同時在雜湊表中儲存指向每個資料行的指標。
只有memory引擎顯示支援雜湊索引,是memory引擎預設索引型別。memory引擎也支援B-Tree索引,支援非唯一雜湊索引。
索引策略
字首索引 :對於BLOB/TEXT或者VARCHAR型別的列,必須使用字首索引,因為Mysql不允許索引這些列的完整長度。
訣竅 選擇足夠長的字首保證較高的選擇性,接近於完整列,使得字首索引的選擇性接近於索引整個列。
多列索引:為每個列建立獨立的索引。是非常錯誤的,比真正最優得到索引可能差幾個數量級。
忽略掉where子句,集中精力優化索引列的順序,或者建立一個全覆蓋索引。
在多個列上建立獨立的單列索引大部分情況下並不能提高MySQL的查詢效能。
MySQL和以上版本引入“索引合併”的策略,一定程度上可以使用表上的多個單列索引來定位指定的行。老版本只能使用其中一個單列索引。
表file_actor在欄位film_id和actor_id上各有一個單列索引。
SELECT film_id,actor_id from sakila.film_actor where actor_id = 1 OR film_id = 1;
在老版本中會全表掃描,除非改成:
SELECT film_id,actor_id from sakila.film_actor where actor_id = 1
UNION ALL
SELECT film_id,actor_id from sakila.film_actor where film_id = 1 and actor_id <>1;
, 在MYSQL5.0和以上版本中,查詢能夠使用者兩個單列索引掃描並進行合併。(需要合併,說明索引建的很槽)
索引聯合消耗了大量的CPU和記憶體資源。
如果在EXPLAIN中看到有索引合併,應該好好檢查一下查詢和表的結構。看是不是已經是最優的。
也可以通過引數optimizer_switch來關閉索引合併功能。也可以使用IGNORE INDEX提示讓優化器忽略掉某些索引。
這演算法的三個變種:OR條件的聯合(union),AND條件的相交(intersection),組合前兩種情況的聯合及相交。
下面就體現了,使用了兩個索引掃描的聯合:--》
選擇合適的索引列順序:在一個多列B-Tree索引中,索引列的順序以為者索引首先按照最左列進行排序,其次是第二列
將選擇性最高的列放在最前面
參考mysql書籍的示例:
聚族索引:InnoDB的聚族索引儲存了B-Tree索引和資料行,聚族索引不是一種單獨的索引型別,而是一種資料儲存方式。
術語“聚族”表示資料行和相鄰的鍵值緊湊地儲存在一起。資料行存放在索引的葉子頁中。
聚族索引的記錄存放方式:葉子頁包含了行的全部資料,節點頁只包含了索引列。下面就是索引列包含的是整數值
InnoDB將通過主鍵聚集資料,也就是圖上的被索引的列就是主鍵列。
冗餘和重複索引:重複索引指在相同的列上按照相同的順序建立相同的型別的索引。如下面
CREATE TABLE test(
ID INT NOT NULL PRIMARY KEY,
A INT NOT NULL,
UNIQUE(ID),
INDEX(ID)
) ENGINE = INNODB;
如果建立了索引(A,B),再建立索引(A)就是冗餘索引,索引(A,B)也可以當作索引(A)來使用。但是如果再建立索引(B,A),則不是冗餘索引,索引(B)也不是,因為B不是索引(A,B)的最左字首列。還有一種情況把一個索引擴充套件為(A,ID),其中ID是主鍵,對於InnoDB來說主鍵列已經包含在二級索引中,所以也是冗餘的。應該儘量擴充套件已有的索引而不是建立新索引。