🍖索引原理與慢查詢優化

引入

本篇博客偏理論, 將會介紹如下知識:html

  • 索引介紹
  • 索引原理
  • 索引的數據結構(二叉樹--->平衡二叉樹--->B樹--->B+樹)
  • 彙集索引與輔助索引
  • MySQL索引管理
  • 建立和刪除索引的語法
  • 建立索引後的測試 (查詢速度的變化)
  • 如何正確使用索引
  • 回表
  • 覆蓋索引
  • 聯合索引
  • 最左前綴匹配
  • 索引下推
  • MySQL查詢優化 : explain
  • 慢查詢優化的基本步驟
  • 慢日誌管理

一.索引介紹

1.什麼是索引

  • 索引是對數據庫表中一列或多列的值進行排序的一種數據結構, 使用索引能夠快速訪問數據庫表中的特定信息
  • 爲數據庫創建索引, 就比如爲書創建目錄

2.爲何要有索引

  • 優化數據查詢效率

數據庫的數據通常存儲在磁盤中, 相比較內存, 磁盤的訪問速度較慢索引就是能夠幫助數據庫快速從磁盤中找到數據的一種數據結構python

  • 注意 : 建立索引後會下降增、刪、改的效率

雖然會下降, 可是通常的應用系統,讀寫比例在10:1左右,並且插入操做和通常的更新操做不多出現性能問題,遇到最多的,也是最容易出問題的,仍是一些複雜的查詢操做,因此查詢語句的優化顯然是重中之重mysql

3.爲表建立的索引是否是越多越好?

  • 多數狀況下, 咱們知道索引可以提升查詢效率, 但過多也會影響應用程序效率, 怎麼加纔是關鍵
  • 一個應用程序的設計, 數據上過多或過少的索引都會引起應用程序的效率問題, 因此咱們須要找到一個平衡點
  • 當表有大量數據的狀況下, 建立索引的速度會很慢, 而且對於寫的性能也會大幅度下降

4.索引應該何時加才最合適

  • 任何一個軟件都有其吸引用戶的亮點, 亮點背後對應的是熱數據, 無疑開發人員對熱數據的所對應的數據庫字段有哪些, 應該在開發軟件的過程當中就提早爲相應的字段加上索引, 而不是等軟件上線後讓DBA發現慢查詢sql後再作處理

緣由 :sql

1.一個軟件慢會影響用戶體驗, 可是慢的緣由有不少, 你不能當即肯定就是 SQL 的問題, 當你定位到 SQL 問題的時候就已通過去好久了, 問題沒有獲得及時的解決數據庫

2.大多數DBA都是管理型DBA而非開發型, 因此即使是DBA從日誌中看到了慢查詢sql, 也會由於其不懂業務而很難分析出慢的緣由windows

二.索引原理

1.索引的原理

  • 經過不斷的縮小想要查詢的數據範圍篩選出最終的結果

就好比買火車票(無索引) : 若是沒有12360火車票訂購軟件, 擺在咱們面前的就是成千上萬輛火車, 選擇那一輛的條件有火車類型、出發和終點、時間等等, 咱們須要一輛一輛火車去比對本身的篩選條件, 運氣好第一輛就是要找的火車, 運氣很差第一千輛纔是要找的火車緩存

加入索引 : 如今咱們只須要在12360軟件上選擇高鐵, 就能篩選掉不是高鐵的火車, 縮小了查詢範圍; 再輸入出發點和終點, 又縮小了查詢範圍; 再輸入時間, 範圍又減小, 最終找到本身須要的車次, 由不固定查詢次數變成很小的固定查詢次數數據結構

2.磁盤I/O與預讀

  • I\O延遲

IO延遲 = 平均尋道時間 + 平均延遲時間(通常爲9ms)--->例子:假設當前硬盤轉軸(盤片)轉速是7200/min,也就是120/s,那麼轉一圈須要花費1/120≈8ms,半圈也就是4ms(假設找到數據要半圈)函數

9ms左右對於咱們來說很短, 但對於一臺500-MIPS的機器來講每秒能夠執行5億條指令, 換句話說執行一次IO的時間能夠執行40萬條指令,數據庫動輒十萬百萬乃至千萬級數據,每次9毫秒的時間, 這簡直是場災難性能

  • 預讀

考慮到磁盤IO是很是高昂的操做,計算機操做系統作了一些優化,當一次IO時,不光把當前磁盤地址的數據,而是把相鄰的數據也都讀取到內存緩衝區內,由於局部預讀性原理告訴咱們,當計算機訪問一個地址的數據的時候,與其相鄰的數據也會很快被訪問到。每一次IO讀取的數據咱們稱之爲一頁(page)。具體一頁有多大數據跟操做系統有關,通常爲4k或8k,也就是咱們讀取一頁內的數據時候,實際上才發生了一次IO,這個理論對於索引的數據結構設計很是有幫助

三.索引的數據結構

索引的數據結構是 B+樹, 而 B+樹 是通過 二叉排序樹 到 二叉平衡樹 再到 B樹 最後到 B+樹 演變過來的, 下面簡單介紹一下:

1.二叉排序樹(二叉查找樹)

  • 頂端的節點咱們稱爲根節點,沒有子節點的節點咱們稱之爲葉節點(就是最下面一排)

對於一列數字 : 五、六、七、八、九、10

image-20210224174432982

  • 若是咱們須要找到 key=9 的節點, 先將 9 與根節點比較, 大於根節點, 因而往右邊找; 繼續與右邊的 10 比較, 小於, 因而往左邊找, 正好找到九

利用二叉排序樹咱們只須要3次便可找到匹配的數據; 若是在數字列中一條條的查找的話,咱們須要5次才能找到

2.平衡二叉樹(AVL樹)

  • 平衡二叉樹能夠說是二叉排序樹的改進版, 是特殊的二叉排序樹

上面咱們講解了利用二叉排序樹能夠快速的找到數據; 可是,若是上面的二叉排序樹是這樣的構造:

image-20210224175721158

平均查找長度是3, 若是咱們調整一下關鍵字的序列

image-20210224180204999

調整以後平均查找長度是 2.2, 從上面咱們能夠看出平均查找長度與數的高度有關, 平均查找長度越小, 查找速度就越快, 因此咱們應該儘量的讓這棵樹矮

  • 怎麼判斷一顆二叉樹是不是平衡二叉樹?

這裏引入了平衡因子的概念, 左子樹的高度減右子數的高度就是平衡因子, 平衡因子的絕對值小於或等於一就是平衡二叉樹, 大於一就是非平衡二叉樹, 以下圖平衡因子爲 4 就是非平衡二叉樹

image-20210224181012776

咱們調整一下關鍵字序列, 各子數平衡因子絕對值都小於或等於 1, 那麼這就是一顆平衡二叉樹

image-20210224181425555

3.B 樹 ( Balanced Tree)多路平衡查找樹

  • 咱們知道平衡二叉樹每一個節點只能存儲一個鍵值和數據

若是咱們要存儲海量的數據呢?能夠想象到二叉樹的節點將會很是多,高度也會及其高,咱們查找數據時也會進行不少次磁盤IO,咱們查找數據的效率將會極低

  • 爲了解決平衡二叉樹的這個弊端,咱們應該尋找一種單個節點能夠存儲多個鍵值和數據的平衡樹, 也就是 B樹

img

從上圖能夠看出,B樹相對於平衡二叉樹,每一個節點(B樹中節點稱之爲頁)存儲了更多的鍵值(key)和數據(data),而且每一個節點擁有更多的子節點,子節點的個數通常稱爲階,上述圖中的B樹爲3階B樹,高度也會很低。 基於這個特性,B樹查找數據讀取磁盤的次數將會不多,數據的查找效率也會比平衡二叉樹高不少

假設每一個節點能夠儲存兩個值(不表明只能存兩個), 咱們找到75:

  • 先與 頁1 比較,在 35 右邊找到 p3指針 定位到 頁4
  • 與 頁4 中的索引對比, 在 65-87 之間, 找到指針 p2, 定位到 頁10
  • 與 頁10 中的索引對比, 找到相對應的 75

4.B+ 樹

  • B+ 樹是對 B樹的進一步優化

img

  • 經過上圖咱們來對比下 B+ 樹與 B樹的不一樣:
  • B+ 樹非葉子節點上是不存儲數據的,僅存儲鍵值,而 B 樹節點中不只存儲鍵值,也會存儲數據

  • 之因此這麼作是由於在數據庫中頁的大小是固定的,InnoDB 中頁的默認大小是 16KB。若是不存儲數據,那麼就會存儲更多的鍵值,相應的樹的階數(節點的子節點樹)就會更大,樹就會更矮更胖,如此一來咱們查找數據進行磁盤的 IO 次數又會再次減小,數據查詢的效率也會更快

  • B+ 樹的階數是等於鍵值的數量的,若是咱們的 B+ 樹一個節點能夠存儲 1000 個鍵值,那麼 3 層 B+ 樹能夠存儲 1000×1000×1000=10 億個數據。

  • 通常根節點是常駐內存的,因此通常咱們查找 10 億數據,只須要 2 次磁盤 IO

  • 3層的b+樹能夠表示上百萬的數據,若是上百萬的數據查找只須要兩次IO,性能提升將是巨大的,若是沒有索引,每一個數據項都要發生一次IO,那麼總共須要百萬次的IO,顯然成本很是很是高

  • B+ 樹的兩種性質
  • 索引字段要儘可能的小 : 磁盤塊的大小也就是一個數據頁的大小,是固定的. 若是數據項佔的空間越小,數據項的數量越多,樹的高度就越低, 查詢過的IO次數就越少. 這就是爲何每一個數據項,即索引字段要儘可能的小,好比int佔4字節,要比bigint8字節少一半。這也是爲何b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度降低, 降低則會致使每層可存儲的數據就少, 由於磁盤塊是固定的, 從而要增長層次, 進而致使樹增高, 樹增高意味着找到底層數據的IO次數增多, 致使查詢速度大幅度降低
  • 索引的最左匹配特性 : 當b+樹的數據項是複合的數據結構,好比(name,age,sex)的時候,b+數是按照從左到右的順序來創建搜索樹的. 好比當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據. 但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。 好比當(張三,F)這樣的數據來檢索時,b+樹能夠用name來指定搜索方向,但下一個字段age的缺失,因此只能把名字等於張三的數據都找到,而後再匹配性別是F的數據了, 這個是很是重要的性質,即索引的最左匹配特性

5.總結 B+ 樹優勢

  • 在二叉樹、平衡二叉樹、B樹的基礎上作了進一步優化, 只有葉子節點放真正的數據,這意味着在等量數據的前提下,B+樹的高度是最低的
  • B+的葉子節點都是排好序的,這意味着在範圍查詢上,B+樹比B樹更快,快就快在一旦找到一個樹葉節點,就不須要在再從樹根查起了

四.彙集索引與輔助索引

數據庫中的 B+樹 索引能夠分爲彙集索引(clustered index)和輔助索引(secondary index), 彙集索引與輔助索引相同的是:無論是彙集索引仍是輔助索引,其內部都是B+樹的形式,即高度是平衡的, 不一樣的是 :

彙集索引的葉子節點存放的是一整行完整的信息, 而輔助索引的葉子節點存放的並不是完整信息(下面介紹)

1.彙集索引 (Clustered Index)

InnoDB 彙集索引的葉子節點存儲行記錄,所以 InnoDB 必需要有且只有一個彙集索引

  • 若是表定義了 PK (Primary Key,主鍵),那麼 PK 就是彙集索引

  • 若是表沒有定義 PK,則第一個不爲空且惟一(NOT NULL UNIQUE) 的列就是彙集索引

  • 不然 InnoDB 會另外建立一個隱藏的 ROWID 做爲彙集索引

因爲這種機制是直接定位行記錄,所以使得基於 PK 的查詢速度很是快

2.輔助索引( Secondary Index )

表中除了彙集索引外其餘索引都是輔助索引(Secondary Index,也稱爲非彙集索引)

  • 與彙集索引的區別是:輔助索引的葉子節點不包含行記錄的所有數據。葉子節點除了包含鍵值之外,每一個葉子節點中的索引行中還包含一個書籤(bookmark)。該書籤用來告訴InnoDB存儲引擎去哪裏能夠找到與索引相對應的行數據

  • 輔助索引的存在並不影響數據在彙集索引中的組織,所以每張表上能夠有多個輔助索引,但只能有一個彙集索引

  • 當經過輔助索引來尋找數據時,InnoDB存儲引擎會遍歷輔助索引並經過葉子級別的指針得到指向主鍵索引的主鍵,而後再經過主鍵索引來找到一個完整的行記錄

五.Mysql 索引管理

1.功能

  • 索引的功能就是加速查找
  • mysql中的primary key,unique,聯合惟一也都是索引,這些索引除了加速查找之外,還有約束的功能

2.mysql 中經常使用的索引(鍵)

  • 普通索引: index 加速查找
  • 惟一索引:
    • 主鍵索引: primary key 加速查找+約束
    • 惟一索引: unique key 加速查找+約束
  • 聯合索引:
    • 聯合主鍵索引: primary key
    • 聯合惟一索引: unique(字段1, 字段2, ...)
    • 聯合普通索引: unique(字段1, 字段2, ...)

上面的三種索引, 惟一索引除了能夠增長查詢速度以外各自還具備約束條件, 而普通索引index key沒有任何的約束條件,只是用來幫助你加快速查詢數據

注意:聯合索引不是用來加速查詢用的,不在咱們的而研究範圍以內

3.索引的兩大類型

  • 咱們能夠在建立索引的時候, 爲其指定索引類型(兩類)
hash類型的索引:查詢單條快,範圍查詢慢
btree類型的索引:b+樹,層數越多,數據量指數級增加(咱們就用它,由於innodb默認支持它)
  • 不一樣的存儲引擎支持的索引類型也不同
InnoDB 支持事務,支持行級別鎖定,支持 B-tree、Full-text 等索引,不支持 Hash 索引
MyISAM 不支持事務,支持表級別鎖定,支持 B-tree、Full-text 等索引,不支持 Hash 索引
Memory 不支持事務,支持表級別鎖定,支持 B-tree、Hash 等索引,不支持 Full-text 索引
NDB 支持事務,支持行級別鎖定,支持 Hash 索引,不支持 B-tree、Full-text 等索引
Archive 不支持事務,支持表級別鎖定,不支持 B-tree、Hash、Full-text 等索引

六.建立和刪除索引語法

1.建立的三種方法

  • 語法
🍓方式一 : 建立表時建索引
create table [表名] (
    [unique|fulltext|spatial] [index|key] [索引名] [字段名(長度)] [asc|desc]
    );
    
🍓方式二 : 在已存在的表上建立
create [unique|fulltext|spatial] index [索引名]
    on [表名] [字段名(長度)] [asc|desc];

🍓方式二 : alter 在已存在的表上建立索引
alter table [表名] add [unique|fulltext|spatial] index 
    [索引名] [字段名(長度)] [asc|desc];
  • 示例代碼
🍓方式一
create table t01(
    id int,
    name char(10),
    age int,
    sex enum("male","female"),
    unique key unique_id(id),
    index index_name(name)  # index沒有key
);

🍓方式二
create index index_age on t01(age);

🍓方式三
alter table t01 add index index_sex(sex);

2.刪除索引

drop index [索引名] on t01;    # 語法
drop index index_age on t01;  # 示例

七.索引測試

1.先準備一張表, 並插入大量的數據

🍓建立表
create table t01(
    id int,
    name varchar(10),
    sex enum("male","female"),
    email varchar(18)
);

🍓建立存儲過程,進行自動插入記錄
delimiter %%%
create procedure p01()
begin
    declare i int default 1;
    while(i<3000000)do
        insert t01 value(i,"shawn","male",concat("shawn",i,"@163.com"));
        set i=i+1;
    end while;
end %%%
delimiter ;

🍓查看存儲過程
show create procedure p01\G  # \G 垂直顯示結果

🍓調用存儲過程
call p01();  # windows執行測試大概一個半小時,3百萬條記錄,200多M

🍓刪除存儲過程
drop procedure p01;

image-20210225095534649

2.沒有創建索引的狀況下測試查詢速度

select * from t01 where id=3000000;

沒有索引,mysql不知道有沒有這條記錄, 因此從頭至尾的對記錄進行遍歷,有多少磁盤塊就要進行多少I\O,速度很慢

image-20210225095645650

3.爲表的某個字段創建索引(表已經存在大量記錄,建立速度會很慢)

create index index_id on t01(id);  # 爲 id 字段創建普通索引

觀察 data 文件夾下的 t01 表數據文件大小增長了

image-20210225100323340

image-20210225110724526

4.使用創建了索引的字段設置爲條件進行查詢

select * from t01 where id=3000000;  # 能夠觀察到速度明顯的提高

image-20210225101246174

5.步驟分析

  • mysql先去索引表裏根據 b+樹 的搜索原理很快搜索到 id 等於3000000的記錄,直接命中索引, IO大大下降,於是速度明顯提高

  • 咱們以沒有創建索引的字段設置爲條件來進行查詢, 能夠發現速度依然很慢

    select * from t01 email="shawn3000000@163.com";  # 而且記錄越大查詢越慢

    image-20210225110358350

  • 對 email 字段創建索引試試

create index index_email on t01(email);  # 字段數據越大,創建的時間越長(因此建議不要使用數據很大的字段創建索引,這裏只是作實驗)
select * from t01 where email="shawn3000000@163.com";  # 再去查詢,能夠發現速度是數量級的提高

image-20210225111132774

八.正確使用索引

並非說建立了索引就必定能加速查詢, 有些狀況就算命中了索引也未必能起到很好的提速效果, 下面來測試一下各類狀況 (若是不想看過程,能夠直接看小結末尾的結論)

1.範圍查詢狀況 : 或者說條件不明確, 條件中有 : >、>=、<、<=、!= 、between...and...、like

  • 大小於 : >、<

image-20210225145700684

  • 不等於 : !=

image-20210225145844390

  • between...and...

image-20210225150055303

  • 模糊匹配 : like

like

2.區分度狀況 : 區分度表示字段不重複的比例, 區分度越大, 掃描的速度就越快,像主鍵惟一, 而性別字段區分度就很低

image-20210225153807708

出現上面的狀況就是由於字段的區分度過低, 在 B+樹 中對於這些字段沒法比較大小, 由於值都是相等的, 毫無疑問,只能增長樹的高度來保證這些數據的存儲, 樹的高度越高, 查詢速度就越慢

3.=和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式

4.索引列不要參與計算或者函數, 好比薪資乘與12 : 不要使用字段相乘(salary*12=10000), 可使用該字段所對應的值相乘(salary=10000*12)

image-20210225155533918

5.索引下推技術

🍓"and"與"or"的邏輯
    [條件1] and [條件2] : 全部條件都成立纔算成立,但凡要有一個條件不成立則最終結果不成立
    [條件1] or [條件2] : 只要有一個條件成立則最終結果就成立

🍓"and"的工做原理
    條件:        
        a = 10 and b = 'xxx' and c > 3 and d =4    
    索引:        
        製做聯合索引(d,a,b,c)    
    工做原理:        
        對於連續多個and:mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣即可以快速鎖定很小的範圍),加速查詢,即按照d—>a->b->c的順序

🍓"or"的工做牌原理
    條件:        
        a = 10 or b = 'xxx' or c > 3 or d =4    
    索引:        
        製做聯合索引(d,a,b,c)            
    工做原理:        
        對於連續多個or:mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d

6.最左前綴匹配原則

7.總結 (怎麼創建索引能提升查詢效率)

  • 對區分度高而且佔用空間小的字段創建索引
  • 針對範圍查詢命中了索引,若是範圍很大,查詢效率依然很低,如何解決
    • 要麼把範圍縮小
    • 要麼就分段取值,一段一段取最終把大範圍給取完
  • 不要把查詢字段放到函數或者參與運算
  • 索引下推技術,mysql自動選擇查詢速度最優的那條語句 (默認是開啓)
  • 索引覆蓋 (下面介紹)
  • 最左前綴匹配原則 (下面介紹)

8.其餘注意事項

  • 避免使用select *
  • count(1)或count(列) 代替 count(*)
  • 建立表時儘可能時 char 代替 varchar
  • 表的字段順序固定長度的字段優先
  • 組合索引代替多個單列索引(常用多個條件查詢時)
  • 儘可能使用短索引
  • 使用鏈接(JOIN)來代替子查詢(Sub-Queries)
  • 連表時注意條件類型需一致
  • 索引散列值(重複少)不適合建索引,例:性別不適合

九.回表、覆蓋索引、聯合索引、最左前綴

匹配原則、索引下推

1.建立一張表, 並插入記錄

create table user(
    -> id int not null auto_increment,
    -> name char(16) not null,
    -> age int not null,
    -> primary key(id),            # id 爲主鍵並設置索引(彙集索引)
    -> index index_name(name));    # name 字段設置索引(輔助索引)

insert user(name,age) value
    -> ("shawn",23),
    -> ("song",22),
    -> ("hai",20),
    -> ("xing",18),
    -> ("yanxi",45),
    -> ("zichen",25);

image-20210225170144065

2.回表

select * from user where id=2;

上面爲主鍵查詢方式, 即經過彙集索引, 能找到 id 爲 2 的完整記錄

select * from user where name="song";

上面爲輔助索引查詢方式, 則須要先搜索 name 索引樹,獲得 song 對應的 id 值爲 2,再到 id 索引樹搜索一次, 這個過程稱爲回表

  • 結論 : 因此基於輔助索引的查詢須要多掃描一棵索引樹。所以,咱們在應用中應該儘可能使用匯集索引

3.覆蓋索引

select id from user where name="hai";

上面語句查詢的條件是 name 字段, name 字段有索引樹, 而且上面保存有 name 和 id 的值, 能夠直接提供查詢結果, 不須要進行回表操做, 也就是說, 在這個查詢裏面, 索引 name 已經覆蓋了咱們所要查詢的 id 字段需求, 這就稱爲覆蓋索引

select age from user where name="xing";

上面語句經過 name 索引樹找到 name 字段對應的 "xing" 和 id 值, 但沒有 age 字段信息, 因而經過 id 字段進行回表操做查找到知足條件的數據

  • 回表操做無疑是下降效率的, 咱們能夠再爲 age 創建索引, 避免索引太多可能引發的效率問題, 也能夠爲其創建聯合索引

4.聯合索引、最左匹配原則

🔰聯合索引是指對錶上的多個列合起來作一個索引. 聯合索引的建立方法與單個索引的建立方法同樣,不一樣之處在僅在於有多個索引列

🔰最左前綴匹配原則, 是很是重要的原則, mysql會從左到右進行匹配

  • 先刪除 name 字段的索引, 再與 age 一塊兒創建聯合索引
drop index index_name on user;
create index index_name_age on user(name,age);  # 實際應用中應該把最經常使用的字段放在最左邊
  • 當查詢條件中出現如下字段,能夠命中聯合索引,由於符合最左前綴原則
select name,age from user where name="song";  # 條件字段 name
select name,age from user where name="song" and age>18;  # 條件字段 name + age
  • 查詢條件中只出現一個 age 字段,不能命中聯合索引
select name,age from user where age=2;  # 條件字段 age (不會走聯合索引)

5.索引下推 (mysql自動開啓)

索引下推(index condition pushdown )簡稱ICP,在Mysql5.6的版本上推出,用於優化查詢

使用最左前綴匹配原則 + 聯合查詢能夠加快查詢速度, 若是咱們的條件存在範圍查詢, 那麼 SQL 語句是怎麼運行的呢?

select * from user where name like "s%" and age=22;

image-20210225180632714

如上表的記錄, "s" 開頭的記錄有兩條

  • Mysql 5.6 之前 沒有索引遞推這個優化
Innodb 會忽略 age 這個字段, 直接經過 name 來進行查詢, 在(name,age)這個聯合索引上找到兩條結果, 而後拿到 id 爲 1 和 2 進行"兩次回表查詢"
  • Mysql 5.6 及之後 添加了索引下推這個優化
Innodb 不會忽略 age 這個字段, 而是在索引內部就判斷了 age 是否等於 22, 不等於 22 的記錄直接跳過
所以在(name,age)這個聯合索引上只匹配到一個記錄, 此時拿着這一個 id 去回表到全部的數據只須要"回表一次"

十.MySQL查詢優化 : explain

官方文檔 : https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

ps : 強調 rows 是核心指標,絕大部分 rows 小的語句執行必定很快。因此優化語句基本上都是在優化rows

1.explain 是什麼

explain 簡稱查看執行計劃,使用 explain 關鍵字能夠模擬優化器執行SQL查詢語句,從而知道MySQL 是如何處理 SQL 語句的

2.explain 如何使用

語法 : explain + [SQL語句]

3.explain 的做用

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

4.執行計劃表包含的字段信息

explain select * from t01;

image-20210225183254836

5.計劃表字段說明

  • id 字段
字段 說明
id MySQL Query Optimizer 選定的執行計劃中查詢的序列號。表示查詢中執行 select 子句或操做表的順序,id值越大優先級越高,越先被執行; 若id 相同,執行順序由上至下
  • select_type 字段
select_type 查詢類型 說明
SIMPLE 簡單的 select 查詢,不使用 union 及子查詢
PRIMARY 最外層的 select 查詢
UNION UNION 中的第二個或隨後的 select 查詢,不 依賴於外部查詢的結果集
DEPENDENT UNION UNION 中的第二個或隨後的 select 查詢,依 賴於外部查詢的結果集
SUBQUERY 子查詢中的第一個 select 查詢,不依賴於外 部查詢的結果集
DEPENDENT SUBQUERY 子查詢中的第一個 select 查詢,依賴於外部 查詢的結果集
DERIVED 用於 from 子句裏有子查詢的狀況。 MySQL 會 遞歸執行這些子查詢, 把結果放在臨時表裏
UNCACHEABLE SUBQUERY 結果集不能被緩存的子查詢,必須從新爲外 層查詢的每一行進行評估
UNCACHEABLE UNION UNION 中的第二個或隨後的 select 查詢,屬 於不可緩存的子查詢
  • table 字段
字段 說明
table 輸出行所引用的表
  • type 字段

很是重要的項, 顯示鏈接使用的類型, 按最優到最差的類型排序

type : 鏈接類型 說明
system 表僅有一行(=系統表)。這是 const 鏈接類型的一個特例
const const 用於用常數值比較 PRIMARY KEY 時。當 查詢的表僅有一行時,使用 System
eq_ref const 用於用常數值比較 PRIMARY KEY 時。當 查詢的表僅有一行時,使用 System
ref 鏈接不能基於關鍵字選擇單個行,可能查找 到多個符合條件的行。 叫作 ref 是由於索引要 跟某個參考值相比較。這個參考值或者是一 個常數,或者是來自一個表裏的多表查詢的 結果值
ref_or_null 如同 ref, 可是 MySQL 必須在初次查找的結果 裏找出 null 條目,而後進行二次查找。
index_merge 說明索引合併優化被使用了
unique_subquery 在某些 IN 查詢中使用此種類型,而不是常規的 ref:value IN (SELECT primary_key FROM single_table WHERE some_expr)
index_subquery 在 某 些 IN 查 詢 中 使 用 此 種 類 型 , 與 unique_subquery 相似,可是查詢的是非惟一 性索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
range 只檢索給定範圍的行,使用一個索引來選擇 行。key 列顯示使用了哪一個索引。當使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操做符,用常量比較關鍵字列時,可 以使用 range
index 全表掃描,只是掃描表的時候按照索引次序 進行而不是行。主要優勢就是避免了排序, 可是開銷仍然很是大
all 最壞的狀況,從頭至尾全表掃描
  • possible_keys 字段
字段 說明
possible_keys 指出 MySQL 能在該表中使用哪些索引有助於 查詢。若是爲空,說明沒有可用的索引
  • key 字段
字段 說明
key MySQL 實際從 possible_key 選擇使用的索引。 若是爲 NULL,則沒有使用索引。不多的狀況 下,MYSQL 會選擇優化不足的索引。這種情 況下,能夠在 SELECT 語句中使用 USE INDEX (indexname)來強制使用一個索引或者用 IGNORE INDEX(indexname)來強制 MYSQL 忽略索引
  • key_len 字段
字段 說明
key_len 使用的索引的長度。在不損失精確性的狀況 下,長度越短越好。
  • ref 字段
字段 說明
ref 顯示索引的哪一列被使用了
  • rows 字段
字段 說明
rows MYSQL 認爲必須檢查的用來返回請求數據的行數
  • Extra 字段
extra 項 說明
Using filesort 表示 MySQL 會對結果使用一個外部索引排序,而不是從表裏按索引次序讀到相關內容。可能在內存或者磁盤上進行排序。MySQL 中沒法利用索引完成的排序操做稱爲「文件排序」
Using temporary 表示 MySQL 在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by

6.示例

explain select * from t01 where id=100000;
explain select * from t01 where id>10000 and id<20000;
explain select * from t01 where id>20000;

image-20210225185120221

十一.慢查詢優化的基本步驟

  • 先運行看看是否真的很慢,注意設置SQL_NO_CACHE
  • where條件單表查,鎖定最小返回記錄表。這句話的意思是把查詢語句的where都應用到表中返回的記錄數最小的表開始查起,單表每一個字段分別查詢,看哪一個字段的區分度最高
  • explain查看執行計劃,是否與1預期一致(從鎖定記錄較少的表開始查詢)
  • order by limit 形式的sql語句讓排序的表優先查
  • 瞭解業務方使用場景
  • 加索引時參照建索引的幾大原則
  • 觀察結果,不符合預期繼續從第一個步驟開始分析

十二.慢日誌管理

慢日誌管理 : https://www.cnblogs.com/songhaixing/p/14448814.html

相關文章
相關標籤/搜索