關於MySQL索引知識與小妙招 — 學到了!

1、索引基本知識

1.1 索引的優勢

  1. 大大減小了服務器須要掃描的數據量,加快數據庫的檢索速度
  2. 幫助服務器避免排序和臨時表
  3. 將隨機io變成順序io

1.2 索引的用處

  1. 速查找匹配WHERE子句的行
  2. 從consideration中消除行,若是能夠在多個索引之間進行選擇,mysql一般會使用找到最少行的索引
  3. 若是表具備多列索引,則優化器可使用索引的任何最左前綴來查找行
  4. 當有錶鏈接的時候,從其餘表檢索行數據
  5. 查找特定索引列的min或max值
  6. 若是排序或分組時在可用索引的最左前綴上完成的,則對錶進行排序和分組
  7. 在某些狀況下,能夠優化查詢以檢索值而無需查詢數據行

1.3 索引的分類

數據庫會默認建立索引,可是並非給主鍵創建索引,而是給惟一鍵建裏索引的,由於主鍵的特性是惟一且非空java

  • 主鍵索引: 是一種特殊的惟一索引,不容許有空值。(主鍵約束,就是一個主鍵索引)
  • 惟一索引: 索引列中的值必須是惟一的,可是容許爲空值。
  • 普通索引: MySQL中基本索引類型,沒有什麼限制,容許在定義索引的列中插入重複值和空值,純粹爲了查詢數據更快一 點。
  • 全文索引: 只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT類型字段上使用全文索引

    什麼是全文索引,就是在一堆文字中,經過其中的某個關鍵字等,就能找到該字段所屬的記錄行,好比有"LOL LPL 牧小農" 經過牧小農,可能就能夠找到該條記錄。這裏說的是可能,由於全文索引的使用涉及了不少細節,咱們只須要知道這個大概意思。通常開發中,不貴用到全文索引,由於其佔用很大的物理空間和下降了記錄修改性,故較爲少用。mysql

  • 組合索引: 在表中的多個字段組合上建立的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引纔會被使用,使用組合索引時遵循最左前綴集合。面試

    例如這裏由id、name和age3個字段構成的索引,索引行中就按id/name/age的順序存放,索引能夠索引下面字段組合(id,name,age)、(id,name)或者(id)。若是要查詢的字段不構成索引最左面的前綴,那麼就不會是用索引,好比,age或者(name,age)組合就不會使用索引查詢。sql

    1.4 面試技術名詞

  • 回表: 數據庫根據索引(非主鍵)找到了指定的記錄所在行後,還須要根據主鍵再次到數據塊裏獲取數據,這種稱之爲回表
  • 覆蓋索引: 看我寫的一篇文章:面試三輪我倒在了一道sql題上——sql性能優化
  • 最左匹配: 指在聯合索引中,若是你的 SQL 語句中用到了聯合索引中的最左邊的索引,那麼這條 SQL 語句就能夠利用這個聯合索引去進行匹配,若是遇到範圍查詢(>、<、between、like)就會中止匹配。數據庫

    select from t where a=1 and b=1 and c =1; #這樣能夠利用到定義的索引(a,b,c),用上a,b,c
    select
    from t where a=1 and b=1; #這樣能夠利用到定義的索引(a,b,c),用上a,b
    select from t where b=1 and a=1; #這樣能夠利用到定義的索引(a,b,c),用上a,c(mysql有查詢優化器)
    select
    from t where a=1; #這樣也能夠利用到定義的索引(a,b,c),用上a
    select from t where b=1 and c=1; #這樣不能夠利用到定義的索引(a,b,c)
    select
    from t where a=1 and c=1; #這樣能夠利用到定義的索引(a,b,c),但只用上a索引,b,c索引用不到緩存

  • 索引下推: 稱爲 Index Condition Pushdown (ICP),這是MySQL提供的用某一個索引對一個特定的表從表中獲取元組」,注意咱們這裏特地強調了「一個」,這是由於這樣的索引優化不是用於多表鏈接而是用於單表掃描,確切地說,是單表利用索引進行掃描以獲取數據的一種方式。

1.5 索引採用的數據結構

1.5.1 哈希表
在這裏插入圖片描述
缺點︰性能優化

一、利用hash存儲的話須要將全部的數據文件添加到內存,比較耗費內存空間
二、若是全部的查詢都是等值查詢,那麼hash確實很快,可是在企業或者實際工做環境中範圍查找的數據更多,而不是等值查詢,所以hash就不太適合了服務器

1.5.2 二叉樹
在這裏插入圖片描述
缺點∶數據結構

不管是二叉樹仍是紅黑樹,都會由於樹的深度過深而形成io次數變多,影響數據讀取的效率ide

1.5.3 B+樹

B樹特色:
一、全部鍵值分佈在整顆樹中
二、搜索有可能在非葉子結點結束,在關鍵字全集內作一次查找,性能逼近二分查找
三、每一個節點最多擁有m個子樹
四、根節點至少有2個子樹
五、分支節點至少擁有m/2顆子樹(除根節點和葉子節點外都是分支節點)
六、全部葉子節點都在同一層、每一個節點最多能夠有m-1個key,而且以升序排列

在這裏插入圖片描述

實例圖說明∶
每一個節點佔用一個磁盤塊,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指針,指針存儲的是子節點所在磁盤塊的地址。兩個關鍵詞劃分紅的三個範圍域對應三個指針指向的子樹的數據的範圍域。以根節點爲例,關鍵字爲16和34,P1指針指向的子樹的數據範圍爲小於16,P2指針指向的子樹的數據範圍爲16~34 ,P3指針指向的子樹的數據範圍爲大於34。

查找關鍵字過程:

  1. 根據根節點找到磁盤塊1,讀入內存。【磁盤I/O操做第1次】
  2. 比較關鍵字28在區間(16,34 ),找到磁盤塊1的指針P2。
  3. 根據P2指針找到磁盤塊3,讀入內存。【磁盤I/O操做第2次】
  4. 比較關健字28在區間(25,31 ),找到磁盤塊3的指針P2。
  5. 根據P2指針找到磁盤塊8,讀入內存。【磁盤I/O 操做第3次】
  6. 在磁盤塊8中的關健寧列表中找到關健字28。

缺點:

  • 每一個節點都有key,同時也包含data,而每一個頁存儲空間是有限的,若是data比較大的話會致使每一個節點存儲的k ey數量變小
  • 當存儲的數據量很大的時候會致使深度較大,增大查詢時磁盤io次數,進而影響查詢性能

1.6 索引匹配方式

全值匹配: 全值匹配指的是和索引中的全部列進行匹配

explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';

匹配最左前綴: 只匹配前面的幾列

explain select * from staffs where name = 'July' and age = '23';

explain select * from staffs where name = 'July';

匹配列前綴: 能夠匹配某一列的值的開頭部分

explain select * from staffs where name like 'J%';

explain select * from staffs where name like '%y';

匹配範圍值: 能夠查找某一個範圍的數據

explain select * from staffs where name > 'Mary';

精確匹配某一列並範圍匹配另一列:能夠查詢第一列的所有和第二列的部分

explain select * from staffs where name = 'July' and age > 25;

只訪問索引的查詢: 查詢的時候只須要訪問索引,不須要訪問數據行,本質上就是覆蓋索引

explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';

2、哈希索引

  • 基於哈希表的實現,只有精確匹配索引全部列的查詢纔有效
  • 在mysql中,只有memory的存儲引擎顯式支持哈希索引
  • 哈希索引自身只需存儲對應的hash值,因此索引的結構十分緊湊,這讓哈希索引查找的速度很是快

2.1 哈希索引的限制

  1. 哈希索引只包含哈希值和行指針,而不存儲字段值,索引不能使用索引中的值來避免讀取行
  2. 哈希索引數據並非按照索引值順序存儲的,因此沒法進行排序
  3. 哈希索引不支持部分列匹配查找,哈希索引是使用索引列的所有內容來計算哈希值
  4. 哈希索引支持等值比較查詢,也不支持任何範圍查詢
  5. 訪問哈希索引的數據很是快,除非有不少哈希衝突,當出現哈希衝突的時候,存儲引擎必須遍歷鏈表中的全部行指針,逐行進行比較,直到找到全部符合條件的行
  6. 哈希衝突比較多的話,維護的代價也會很高

2.2 案例

當須要存儲大量的URL,而且根據URL進行搜索查找,若是使用B+樹,存儲的內容就會很大:select id from url where url=""

也能夠利用將url使用CRC32作哈希,可使用如下查詢方式:select id fom url where url="" and url_crc=CRC32("")

此查詢性能較高緣由是使用體積很小的索引來完成查找

3、組合索引

當包含多個列做爲索引,須要注意的是正確的順序依賴於該索引的查詢,同時須要考慮如何更好的知足排序和分組的須要

案例: 創建組合索引 a,b,c ,不一樣SQL語句使用索引狀況

語句 索引是否發揮做用
where a=3 是,只使用了a
where a=3 and b=5 是,使用了a,b
where a =3 and b = 5 and c= 4 是,使用了a,b,c
where a = 3 or c = 4
where a = 3 and c= 4 是,僅使用了a
where a = 3 and b > 10 and c = 7 是,使用了a,b
where a = 3 and b like '%mxn%' and c=7 使用了a

4、聚簇索引與非聚簇索引

4.1 聚簇索引

  • 不是單獨的索引類型,而是一種數據存儲方式,指的是數據行跟相鄰的鍵值緊湊的存儲在一塊兒,將數據存儲與索引放到了一塊,找到索引也就找到了數據
  • 若是沒有定義主鍵,InnoDB會選擇一個惟一的非空索引代替。若是沒有惟一索引,InnoDB會隱式定義一個主鍵來做爲聚簇索引。InnoDB 只彙集在同一個頁面中的記錄。包含相鄰健值的頁面可能相距甚遠。

4.2 非聚簇索引

  • 數據文件跟索引文件分開存放,將數據存儲於索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam經過key_buffer把索引先緩存到內存中,當須要訪問數據時(經過索引訪問數據),在內存中直接搜索索引,而後經過索引找到磁盤相應數據,這也就是爲何索引不在key buffer命中時,速度慢的緣由

  • 經過葉子節點指針找到數據頁中的數據,因此非聚簇索引是邏輯順序

5、覆蓋索引

5.1 基本介紹

  1. 若是一個索引包含全部須要查詢的字段的值,咱們稱之爲覆蓋索引
  2. 不是全部類型的索引均可以稱爲覆蓋索引,覆蓋索引必需要存儲索引列的值
  3. 不一樣的存儲實現覆蓋索引的方式不一樣,不是全部的引擎都支持覆蓋索引,memory不支持覆蓋索引

5.2 優點

一、索引條目一般遠小於數據行大小,若是隻須要讀取索引,那麼mysql就會極大的較少數據訪問量
二、由於索引是按照列值順序存儲的,因此對於IO密集型的範圍查詢會比隨機從磁盤讀取每一行數據的IO要少的多
三、一些存儲引擎如MYISAM在內存中只緩存索引,數據則依賴於操做系統來緩存,所以要訪問數據須要一次系統調用,這可能會致使嚴重的性能問題
四、因爲INNODB的聚簇索引,覆蓋索引對INNODB表特別有用

5.3 案例演示

一、當發起一個被索引覆蓋的查詢時,在explain的extra列能夠看到using index的信息,此時就使用了覆蓋索引
二、在大多數存儲引擎中,覆蓋索引只能覆蓋那些只訪問索引中部分列的查詢。不過,能夠進一步的進行優化,可使用innodb的二級索引來覆蓋查詢。

例如:actor使用innodb存儲引擎,並在last_name字段又二級索引,雖然該索引的列不包括主鍵actor_id,但也可以用於對actor_id作覆蓋查詢

6、優化小細節

  • 當使用索引列進行查詢的時候儘可能不要使用表達式,把計算放到業務層而不是數據庫層
  • 儘可能使用主鍵查詢,而不是其餘索引,由於主鍵查詢不會觸發回表查詢
  • 使用前綴索引

    有時候須要索引很長的字符串,這會讓索引變的大且慢,一般狀況下可使用某個列開始的部分字符串,這樣大大的節約索引空間,從而提升索引效率,但這會下降索引的選擇性,索引的選擇性是指不重複的索引值和數據表記錄總數的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,由於選擇性更高的索引可讓mysql在查找的時候過濾掉更多的行。
    ​ 通常狀況下某個列前綴的選擇性也是足夠高的,足以知足查詢的性能,可是對應BLOB,TEXT,VARCHAR類型的列,必需要使用前綴索引,由於mysql不容許索引這些列的完整長度,使用該方法的訣竅在於要選擇足夠長的前綴以保證較高的選擇性,經過又不能太長。

--建立數據表
create table citydemo(city varchar(50) not null);
insert into citydemo(city) select city from city;

--重複執行5次下面的sql語句
insert into citydemo(city) select city from citydemo;

--更新城市表的名稱
update citydemo set city=(select city from city order by rand() limit 1);

--查找最多見的城市列表,發現每一個值都出現45-65次,
select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10;

--查找最頻繁出現的城市前綴,先從3個前綴字母開始,發現比原來出現的次數更多,能夠分別截取多個字符查看城市出現的次數
select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10;
select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10;
--此時前綴的選擇性接近於完整列的選擇性

--還能夠經過另一種方式來計算完整列的選擇性,能夠看到當前綴長度到達7以後,再增長前綴長度,選擇性提高的幅度已經很小了
select count(distinct left(city,3))/count(*) as sel3,
count(distinct left(city,4))/count(*) as sel4,
count(distinct left(city,5))/count(*) as sel5,
count(distinct left(city,6))/count(*) as sel6,
count(distinct left(city,7))/count(*) as sel7,
count(distinct left(city,8))/count(*) as sel8 
from citydemo;

--計算完成以後能夠建立前綴索引
alter table citydemo add key(city(7));

--注意:前綴索引是一種能使索引更小更快的有效方法,可是也包含缺點:mysql沒法使用前綴索引作order by 和 group by。
  • 使用索引掃描來排序

    mysql有兩種方式能夠生成有序的結果:經過排序操做或者按索引順序掃描,若是explain出來的type列的值爲index,則說明mysql使用了索引掃描來作排序
    ​ 掃描索引自己是很快的,由於只須要從一條索引記錄移動到緊接着的下一條記錄。但若是索引不能覆蓋查詢所需的所有列,那麼就不得不每掃描一條索引記錄就得回表查詢一次對應的行,這基本都是隨機IO,所以按索引順序讀取數據的速度一般要比順序地全表掃描慢
    ​ mysql可使用同一個索引即知足排序,又用於查找行,若是可能的話,設計索引時應該儘量地同時知足這兩種任務。
    ​ 只有當索引的列順序和order by子句的順序徹底一致,而且全部列的排序方式都同樣時,mysql纔可以使用索引來對結果進行排序,若是查詢須要關聯多張表,則只有當orderby子句引用的字段所有爲第一張表時,才能使用索引作排序。order by子句和查找型查詢的限制是同樣的,須要知足索引的最左前綴的要求,不然,mysql都須要執行順序操做,而沒法利用索引排序

  • union all,in,or都可以使用索引,可是推薦使用in
  • 範圍列能夠用到索引,範圍條件是:<、>,範圍列能夠用到索引,可是範圍列後面的列沒法用到索引,索引最多用於一個範圍列
  • 強制類型轉換會全表掃描

    create table user(id int,name varchar(10),phone varchar(11));
    alter table user add index idx_1(phone);
    explain select * from user where phone=13800001234;(不會觸發索引)
    explain select * from user where phone='13800001234';(觸發索引)
  • 更新十分頻繁,數據區分度不高的字段上不宜創建索引

    更新會變動B+樹,更新頻繁的字段建議索引會大大下降數據庫性能.
    相似於性別這類區分不大的屬性,創建索引是沒有意義的,不能有效的過濾數據,
    通常區分度在80%以上的時候就能夠創建索引,區分度可使用 count(distinct(列名))/count(*) 來計算

  • 建立索引的列,不容許爲null,可能會獲得不符合預期的結果
  • 當須要進行錶鏈接的時候,最好不要超過三張表,由於須要join的字段,數據類型必須一致
  • 能使用limit的時候儘可能使用limit
  • 單表索引建議控制在5個之內
  • 單索引字段數不容許超過5個(組合索引)
  • 建立索引的時候應該避免如下錯誤概念

    索引越多越好(錯誤)過早優化,在不瞭解系統的狀況下進行優化(錯誤)

相關文章
相關標籤/搜索