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數據類型的列不應該增加索引。這些列的數據量要麼相當大,要麼取值很少,不利於使用索引。
- 頻繁更新的字段不適合創建索引,因爲每次更新不單單是更新記錄,還會更新索引,保存索引文件
- 表記錄太少,不需要創建索引;
- 經常增刪改的表;
索引什麼時候會失效?
- 如果條件中有or,即使其中有條件帶索引也不會使用(這也是爲什麼儘量少用or的原因)。注意:要想使用or,又想讓索引生效,只能將or條件中的每個列都加上索引
- like查詢是以%開頭
- MySQL在使用 != > < between和 and這些時會全變掃描而使索引失效
- is null 和is not null也會失效
-
字符串不加單引號(where name=litao這樣會失效)
-
索引列上的任何操作均會失效(計算,函數,類型轉換)
-
儘量使用覆蓋索引(避免select*,儘量使查詢列和索引列一致)