數據庫索引的原理、分類、優缺點、爲什麼不用B樹、紅黑樹、Hash?什麼時候該用索引?索引什麼時候會失效?

1. 什麼是索引

數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢,更新數據庫中表的數據.

索引的實現通常使用B樹和變種的B+樹(mysql常用的索引就是B+樹)

索引是在存儲引擎中實現的,也就是說不同的存儲引擎,會使用不同的索引。

MyISAM和InnoDB存儲引擎:只支持BTREE索引,也就是說默認使用BTREE,不能夠更換。

MEMORY/HEAP存儲引擎:支持HASH和BTREE索引

2. 索引的種類

MySQL的索引有普通索引,唯一索引,主鍵索引、組合索引、全文索引。

  • 普通索引:MySQL中基本索引類型,沒有什麼限制,允許在定義索引的列中插入重複值和空值,純粹爲了查詢數據更快一 點。
  • 唯一索引:索引列中的值必須是唯一的,但是允許爲空值。
  • 主鍵索引:是一種特殊的唯一索引,不允許有空值。(主鍵約束,就是一個主鍵索引)
  • 組合索引:在表中的多個字段組合上創建的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引纔會被使用,使用組合索引時遵循最左前綴集合。例如,這裏由id、name和age3個字段構成的索引,索引行中就按id/name/age的順序存放,索引可以索引下面字段組合(id,name,age)、(id,name)或者(id)。如果要查詢的字段不構成索引最左面的前綴,那麼就不會是用索引,比如,age或者(name,age)組合就不會使用索引查詢。
  • 全文索引:全文索引,只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT類型字段上使用全文索引,介紹了要求,說說什麼是全文索引,就是在一堆文字中,通過其中的某個關鍵字等,就能找到該字段所屬的記錄行。一般開發中,不貴用到全文索引,因爲其佔用很大的物理空間和降低了記錄修改性,故較爲少用。 

爲什麼要使用聯合索引

  • 減少開銷。建一個聯合索引(col1,col2,col3),實際相當於建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增加寫操作的開銷和磁盤空間的開銷。對於大量數據的表,使用聯合索引會大大的減少開銷!
  • 覆蓋索引。對聯合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那麼MySQL可以直接通過遍歷索引取得數據,而無需回表,這減少了很多的隨機io操作。減少io操作,特別的隨機io其實是dba主要的優化策略。所以,在真正的實際應用中,覆蓋索引是主要的提升性能的優化手段之一。
  • 效率高。索引列越多,通過索引篩選出的數據越少。有1000W條數據的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每個條件可以篩選出10%的數據,如果只有單值索引,那麼通過該索引能篩選出1000W10%=100w條數據,然後再回表從100w條數據中找到符合col2=2 and col3= 3的數據,然後再排序,再分頁;如果是聯合索引,通過索引篩選出1000w10% 10% *10%=1w,效率提升可想而知!

這裏提一下最左前綴原則 

聯合索引(col1,col2,col3),實際相當於建了(col1),(col1,col2),(col1,col2,col3)三個索引。

比如給a,b,c加上索引,那麼a,b可以用到索引,a,c也可以用到索引,但b,c是用不到的。

包括這個a必須要在用到的第一個索引處。

本節參考文獻:鐵柱同學的回答  和這個大佬的回答

 

3. 索引如何創建

# 方法一:創建表時
       CREATE TABLE 表名 (
                 字段名1  數據類型 [完整性約束條件…],
                 字段名2  數據類型 [完整性約束條件…],
                 [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                 [索引名]  (字段名[(長度)]  [ASC |DESC]) 
                 );

 
#方法二:CREATE在已存在的表上創建索引
         CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                      ON 表名 (字段名[(長度)]  [ASC |DESC]) ;
 
 
#方法三:ALTER TABLE在已存在的表上創建索引
         ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                              索引名 (字段名[(長度)]  [ASC |DESC]) ;
                              
#刪除索引:DROP INDEX 索引名 ON 表名字;

******************************舉例********************************************************
1.創建索引
    -在創建表時就創建(需要注意的幾點)
    create table s1(
    id int ,#可以在這加primary key
    #id int index #不可以這樣加索引,因爲index只是索引,沒有約束一說,
    #不能像主鍵,還有唯一約束一樣,在定義字段的時候加索引
    name char(20),
    age int,
    email varchar(30)
    #primary key(id) #也可以在這加
    index(id) #可以這樣加
    );
    -在創建表後在創建
    create index name on s1(name); #添加普通索引
    create unique age on s1(age);添加唯一索引
    alter table s1 add primary key(id); #添加主鍵索引,也就是給id字段增加一個主鍵約束
    create index name on s1(id,name); #添加普通聯合索引
2.刪除索引
    drop index id on s1;
    drop index name on s1; #刪除普通索引
    drop index age on s1; #刪除唯一索引,就和普通索引一樣,不用在index前加unique來刪,直接就可以刪了
    alter table s1 drop primary key; #刪除主鍵(因爲它添加的時候是按照alter來增加的,那麼我們也用alter來刪)

 本節的參考文獻在這裏。

3. Hash、B樹、B+樹和紅黑樹的比較

hash 的查找時間複雜度是O(1)比B+的O(logn)查找時間更短,爲什麼索引不用hash?

可以從B+樹索引的有序性,葉節點被雙向鏈表連接,方便支持範圍查找,以及分批加載至內存這幾個方面回答

這和業務場景有關,如果只查找一個值的話,hash是一個很好的選擇,單數據庫經常會選擇多條,這時候由於B+樹索引有序,並且又有鏈表相連,它的查詢效率比hash就快很多了。而且數據庫中的索引一般是在磁盤上,數據量大的情況可能無法一次裝入內存,B+樹的設計可以允許數據分批加載,同時樹的高度較低,提高查找效率

參考文獻在這

爲什麼不用紅黑樹(可以從內存,以及樹深度和IO次數方面討論這個問題。)

 

  • 紅黑樹必須存在內存裏的,數據庫表太大了,存不進去。
  • 在大規模數據存儲的時候,紅黑樹(二叉查找樹)往往出現由於樹的深度過大而造成磁盤IO讀寫過於頻繁,進而導致效率低下的情況。B樹可以有多個子女,從幾十到上千,可以降低樹的高度。
  • 磁盤IO代價主要花費在查找所需的柱面上,樹的深度過大會造成磁盤IO頻繁讀寫。根據磁盤查找存取的次數往往由樹的高度所決定,紅黑樹查找一個節點最多要查logN層,每一層都是一個內存頁。雖然你只是想找一個節點,但硬盤必須一次讀一個頁,那麼一共logN次IO,消耗太大。

爲什麼不用B樹(可以從葉節點是否存數據,佔用內存空間大小和是否支持範圍查詢這三個方面解釋。 )

 

  •  B+樹的數據都集中在葉子節點,分支節點只負責索引。  b樹的分支節點也有數據 。所以b+樹的樹高會小於B樹,平均的Io次數會遠大於 B+樹。

(比如一個節點是一個頁4096字節,其中每條數據128字節,那麼一個節點只能存32個數據項,那麼對應的孩子節點數最多爲33個,這顯然不夠用。而b+樹內部節點只作爲導向作用,只存一個整數就可以(int型整數32位,消耗4個字節),4096/4=1024個數據項。這樣b+樹的每個節點的孩子數更多,整個樹的高度就更低,大大增加查詢效率。)

  • B+樹索引節點沒有數據。比較小。B樹可以把索引完全加載至內存中。
  • B+樹更擅長範圍查詢。葉子節點數據是按順序放置的雙向鏈表。  B樹範圍查詢只能中序遍歷,做不到範圍查詢。

B樹、B+樹和紅黑樹的參考文獻在這。

4. 索引的優缺點

索引的優點

  • 通過創建索引,可以在查詢的過程中,提高系統的性能
  • 通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性
  • 在使用分組和排序子句進行數據檢索時,可以減少查詢中分組和排序的時間

索引的缺點

  • 創建索引和維護索引要耗費時間,而且時間隨着數據量的增加而增大
  • 索引需要佔用物理空間,如果要建立聚簇索引,所需要的空間會更大
  • 在對錶中的數據進行增加刪除和修改時需要耗費較多的時間,因爲索引也要動態地維護

 

5. 什麼時候該用索引?

  • 主鍵自動建立唯一索引

  • 在經常用在連接(join)的列上,這些列主要是一些外鍵,可以加快連接的速度

  • 在經常需要根據範圍搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的;

  • 頻繁作爲查詢條件(經常where)的字段應該創建索引

  • 查詢中經常(OrderBy)排序的字段(因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間)

  • 查詢中統計(Count)或者分組(Groupby)的字段;

6. 什麼時候不該用索引?

  • 查詢中很少使用或者參考的列不應該創建索引。
  • 只有很少數據值的列也不應該增加索引。例如性別
  • 定義爲text, image和bit數據類型的列不應該增加索引。這些列的數據量要麼相當大,要麼取值很少,不利於使用索引。
  • 頻繁更新的字段不適合創建索引,因爲每次更新不單單是更新記錄,還會更新索引,保存索引文件
  • 表記錄太少,不需要創建索引;
  • 經常增刪改的表;

索引什麼時候會失效?

 

  1. 如果條件中有or,即使其中有條件帶索引也不會使用(這也是爲什麼儘量少用or的原因)。注意:要想使用or,又想讓索引生效,只能將or條件中的每個列都加上索引
  2. like查詢是以%開頭
  3. MySQL在使用 !=   >  <  between和 and這些時會全變掃描而使索引失效
  4. is null 和is not null也會失效
  5. 字符串不加單引號(where name=litao這樣會失效)

  6. 索引列上的任何操作均會失效(計算,函數,類型轉換)

  7. 儘量使用覆蓋索引(避免select*,儘量使查詢列和索引列一致)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章