MySQL InnoDB索引介紹及優化

前言:sql

因爲BOOS要求每月寫一篇文章,所以也申請了本身的博客,說來慚愧,工做了幾年尚未博客,臉略紅。
之前只知道向互聯網索取和吐槽,看到好的文章羨慕並認同,看到差的文章吐槽並嘆息,以爲寫得一點都不負責任。
如今本身寫博客也是懷着一顆忐忑之心,同勉!


正文:數據庫

1、索引概念

索引(index)翻譯爲一個目錄,用於快速定位咱們想要找的數據的位置。例如:咱們把一個數據庫比做一本書,而索引(index)就是書中的目錄,此刻要找到書的某個感興趣的內容,咱們通常是不會整本書翻完再去確認該內容在哪裏,而是經過書的目錄,定位到該內容章節所在頁數,最後直接翻到該頁面服務器

咱們來看看在數據庫中的索引:
全表掃描 VS 索引掃描
以字典爲例,全表掃描就是若是咱們查找某個字時,那麼通讀一遍新華字典,而後找到咱們想要找到的字
而跟全表掃描相對應的就是索引查找,索引查找就是在表的索引部分找到咱們想要找的數據具體位置,而後會到表裏面將咱們想要找的數據所有查出函數

實例:在一張學生表找到一個名字叫Dev的學生
圖片描述工具

左邊全表掃描:須要從第一行開始一行行的掃描,直到找到100008行Dev這個學生的信息爲止,將這個數據返回回來,但有可能該表中還有同名的學生,所以掃描並無結束,一般全表掃描要找到一個數據,是須要將整張表的數據遍歷一遍,而後才能肯定是否將全部數據返回性能

右邊索引掃描:索引查找是根據首字母排序找到D開頭的Dev,若是首字母相同,那麼再根據第二個字母排序找到,以此類推,咱們找到ID爲100008,而後回表查出ID爲100008的數據fetch

結論:所以索引(對應InnoDB)的索引值對應的是主鍵IDui

2、如何找到索引對應的值

InnoDB引擎主要根據
(1)B+tree
(2)二分查找法
圖片描述編碼

B+tree: B+樹擁有整棵樹的根節點、支節點和頁節點,上層會存儲下層節點的管理範圍,直到頁節點的具體信息spa

二分查找法:根據B+樹存儲的各個節點的範圍,進行比較,逐步縮小範圍,最後定位到頁節點中咱們想要的位置

3、介紹下InnoDB表也是一張索引表

圖片描述

如上圖InnoDB表是聚簇表,意思是InnoDB自己是一張大的索引組織表,也是一個根據主鍵排序的大索引的B+樹結構,咱們在InnoDB裏面另外創建本身想要索引的表的字段

聚簇索引就意味着InnoDB表自己,而咱們把這些根據其餘字段排序的索引稱爲二級索引(secondery class)

4、在數據庫中如何創建索引

在MySQL中主要創建兩種類型的索引

1.單列索引

create index idx_name on tb_student(name);
              索引名        表名   字段名

2.聯合索引

create index idx_name_age on tb_student(name,age);
#索引中先根據name排序,name相同的狀況下根據age排序

5、索引維護

首先介紹下什麼是索引維護?這是一個關乎性能的重要概念

若是索引所在字段發生了修改、刪除、插入等操做,那麼索引項就會發生變化,所以若是不能保證索引的有序,那麼就不能索引的準確與效率,而索引的排序發生了變化的這個行爲,咱們稱爲索引維護

在insert/delete/update操做時,爲了維護索引的排序,數據庫會自動的完成索引項的維護,索引的排序,這些行爲對用戶是透明的,感受不到的

在一個有索引的表中,建立它時,實際上還同時建立了索引排序的表,所以在DML中,插入等操做再也不是普通的插入,MySQL將它封裝成了一個事務,連着索引項的排序表一塊兒操做

所以,咱們應當嚴格控制表上的索引數量,不然容易影響數據庫的性能

總結索引維護以下:

一、索引維護由數據庫自動完成
二、插入/修改/刪除每個索引行都變成一個內部封裝的事務
三、索引越多,事務越大,代價越高
四、索引越多,對錶的插入和索引字段的修改就越慢

所以能夠看出索引並不是是越多越好,在工做中也要慎用,尤爲對於寫操做較爲頻繁的業務

6、如何正確的使用索引?

一、依據where查詢條件創建索引

eg:
select a,b from tb_test where c = ?;
idx_c(c)   ->正確

select a,b from tb_test where c = ? and b = ?
idx_cd(c,d)  ->正確

二、根據排序order by ,group by , distinct 字段添加索引

eg:
select * from tb_test order by a;
select a,count(*) from tb_test group by a;
idx_a(a)  ->正確

select * from tb_test order by a,b;
idx_a_b(a,b)  ->正確

select * from tb_test order where c = ? by a;
idx_c_a(c,a)  ->正確

7、到底哪些字段適合建立索引?

一、字段值的重複程度,如圖:
圖片描述

身份證號碼因爲基本上不可能重複,所以選擇性很是好,而人的名字重複性較低,選擇性也不錯, 性別選擇性較差,重複度很是高

二、選擇性不好的字段一般不適合建立索引,但也有例外

如:男女比例相仿的表中,性別不適合建立單列索引,若是走索引不如走全表掃描,
 由於走索引的I/O開銷更大
    
 但若是男女比例極度不平衡,要查詢的又是少數方,如:理工學校、IT公司等能夠考慮使用索引

三、聯合索引中選擇性好的字段應該排在前面

select * from tab_a where gender=? and name=?
idx_name_gender(name,gender)   ->正確

四、聯合索引能夠爲單列、複列查詢提供幫助

idx_smp(a,b,c)
where a=?;                ->正確
where a=? and b=?;        ->正確
where a=? and c=?;        ->正確 (注:須要MySQL5.6版本以上;在5.5及之前版本,能夠對a字段進行索引掃描,但c字段不行)
where a=? and b=? and c=? ->正確

五、合理建立聯合索引,避免冗餘

(a),(a,b),(a,b,c)      ->不可取
(a,b,c)                ->正確,能夠覆蓋前兩個

8、再來看看如何在長字段上創建索引呢

首先,在較長的字段上創建索引是很是影響性能的,好比文章等超大varchar或者text字段,若是不是非建不可,通常不推薦,另外對InnoDB索引單字段(utf8)只能取前767bytes

那麼如何處理長字段索引?

主要根據類型來分別處理:
一、Email類,能夠創建前綴索引
mail_addr varchar(2048)
idx_mailadd(mail_addr(39))   -> 正確
解析:因爲email郵件類型字段,通常後綴都有較大可能相同,如.com .cn等等,
而前綴相同的可能性較低,且郵箱通常長度較短,所以能夠創建前綴索引

二、住址類,分拆字段
home_addr varchar(2048)
idx_homeadd(home_addr(30))    ->錯誤,極可能前半段是相同的省市區街道名

province_add varchar(1024),
city_add varchar(1024), 
district_add `varchar(1024),
lolcal_add varchar(1024)      --創建聯合索引或者單列索引 ->正確

9、對核心SQL索引作覆蓋掃描

對於最核心的SQL,咱們能夠考慮使用索引覆蓋,什麼是索引覆蓋呢,下面是個例子

select name from tb_user where userid=?
key idx_uid_name(userid,name)   ->覆蓋索引掃描

咱們查詢用戶名這種操做頻率很是高,而索引裏面又存儲了字段的值,所以在咱們作查詢時,name字段的值直接在索引中返回,而不須要回表;還有一個使用很是普遍的例子:用戶登錄,咱們能夠將username password作覆蓋索引,這樣大大提升登錄驗證的速度

所以覆蓋索引覆蓋就是將你要查詢的字段和條件字段一塊兒創建聯合索引,這樣的好處是不須要回表獲取name字段,IO最小,速度塊

10、哪些狀況沒法使用索引?

一、索引列進行數據運算或者函數運算

eg:
   where id+1=10;    ->錯誤,沒法利用到索引
   where id=(10-1)   ->正確

   where year(id) < 2016      ->錯誤,沒法利用到索引
   where col < '2016-01-01'   ->正確

二、未含複合索引的前綴字段

idx_abc(a,b,c)
where b=? and c=?   ->錯誤,沒法利用到索引
正確的創建索引方式(b,c)

三、前綴通配符"_" "%"等

like '%ttt%'   ->錯誤,沒法利用到索引
like "ttt%"    ->正確

四、where條件使用NOT,<>,!= 一般也沒法使用到索引

五、字段類型不匹配

字段類型並不絕對匹配時,可能會致使沒法使用索引
a int(11) ,idx_a(a)
where a = '123'   ->錯誤,可能致使未知的錯誤,這個跟編碼有關係
where a = 123     ->正確

11、利用索引作排序操做

以 idx_ab(a,b)索引爲例
一、能使用上述索引進行排序的操做是:

order by a;
a = 3 order by b;
order by a,b;
order by a desc ,b desc;
a > 5 order by a;

二、不能使用索引幫助排序的查詢

order by b; #沒有使用到聯合索引的第一個字段

a > 5 order by b;  #一旦前綴操做是一個range而非=操做,那麼就沒法利用到索引,
這裏 a>5沒法利用索引,二聯合索引的第一個字段未利用,
所以 order by b也沒法利用索引查詢

a in (1,3) order by b; #in裏面的值沒有創建索引,所以沒法利用索引,a未用所以order by b也沒法使用

order by a asc, b desc; #這裏order by a esc是利用了索引,可是b desc未利用到,由於b要和a排序方式一致纔可利用到索引

12、如何肯定一個查詢有沒有走索引,走了哪些索引?

MySQL中自帶命令行工具 explain 來查看一個sql語句是否了索引

使用方式:

explain select * from tb_test;

關注的項:

一、type : 查詢access的方式,表的鏈接類型
      
      index |  索引  
      full  |  全表掃描 
      ref   |  參照查詢,也就是等值查詢  
      range |  範圍查詢
二、key  : 本次查詢最終選擇使用哪一個索引,NULL爲未使用索引
三、key_len : 選擇的索引使用的前綴長度或者整個長度
四、rows    : 查詢邏輯掃描過的記錄行數
五、extra   : 額外信息,主要是指fetch data的具體方式

總結:索引的本質仍是提高咱們查詢數據庫的速度,減小服務器I/O開銷,提供更穩定快捷的服務

注:若有未完善之處,歡迎補充;若有錯誤之處,歡迎指正

相關文章
相關標籤/搜索