基於mysql的數據庫索引使用背后的原理
1:什么是索引
MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數據的數據結構。
簡單來說:索引是一種數據結構
復雜來說:數據庫在肩負着存儲數據之外,還要查詢數據,那么如何查詢數據呢?最基本的,順序查找,但是數據量較大時耗時(O(n)),
優化查詢方式,比如二分查找,需要特定的數據結構支持,所以需要一種數據結構支持高級的搜索算法,這個數據結構就是索引。
2:索引用的什么數據結構?為什么用這種數據結構?
索引的數據結構:b+樹,
原因:數據庫讀取數據涉及到磁盤io,可以從這個角度對比二叉樹/b+;b-/b+;
交代一下背景:機械硬盤時代,索引數據較多;
二叉樹/b+對比:
1:二叉樹:由於索引數據較多,占用空間較大,無法一次加載到內存中,由圖2-1-1至2-2-5所示,樹的高度是4,查詢的值的節點也在第四層,即最壞情況磁盤io次數等於樹的高度。那么如果和優化呢?將瘦高的二叉樹變成矮胖的樹,這樣就可以減少樹的高度,減少io次數,所以出現了b-tree
b-tree/b+對比:

2-1-1

2-1-2

2-1-3

2-1-4

2-1-5
2:b樹
什么是b-tree呢?滿足一下條件:針對m階b-tree,如圖2-2-1,
1:根節點至少有個倆棵子樹,
2:每個分結點至少有個m/2棵子樹,至多有m棵子樹
3:每個葉子節點都在同一層
4:每個分節點最多有個m-1個鍵,m個指針,並且按鍵值升序排列
b-tree的優點:越靠近根節點搜索越快,因為key直接對應data
但是有一個缺點:內存中一頁為16k,b-tree的節點是攜帶data,極端情況下,若數據過多則會導致查詢多次翻頁,多次磁盤io影響性能,此時b+tree應運而生。

2-2-1
3:b+樹,如圖2-3-1
b+tree是對b-tree的優化,通過圖2-2-1 和 2-3-1 的對比可以發現
1:b+tree除了葉子節點外沒有攜帶數據,所以一頁內存可以裝在更多的信息
2:葉子節點以一根指針相連
3:數據都存儲在葉子節點
4:b-/b+對比
最重要的就是磁盤io的次數對比,在這里引用一個理論上的計算:{
InnoDB存儲引擎中頁的大小為16KB,一般表的主鍵類型為INT(占用4個字節)或BIGINT(占用8個字節),指針類型也一般為4或8個字節,也就是說一個頁(B+Tree中的一個節點)中大概存儲16KB/(8B+8B)=1K個鍵值(因為是估值,為方便計算,這里的K取值為〖10〗^3)。也就是說一個深度為3的B+Tree索引可以維護10^3 * 10^3 * 10^3 = 10億 條記錄。
}
實際上每一個節點基本不可能填滿,一般一顆b+tree為2-4層,1-3次檢索。

2-3-1
索引都包含哪些
為什么用索引快,以及可能遇到的問題
1:舉個簡單的例子:有倆百萬的數據,找到最后一條數據id=2000000(倆百萬),是順序查找快還是利用索引直接定位快,顯然索引快,這就是為什么用索引。
2:上面都在說使用索引的好處,但過多的使用索引將會造成濫用。因此索引也會有它的缺點:雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件。建立索引會占用磁盤空間的索引文件。一般情況這個問題不太嚴重,但如果你在一個大表上創建了多種組合索引,索引文件的會膨脹很快。索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。下面是一些總結以及收藏的MySQL索引的注意事項和優化方法。
單獨說一下 聚集索引和非聚集索引
聚集索引:
是按照數據存放的物理位置為順序
非聚集索引:
是按照數據存放的邏輯順序為順序,
其他的索引,比如唯一索引可能是聚集索引,都是屬於聚集索引或者非聚集索引
普通索引
在某一類上創建索引;
–直接創建索引
CREATE INDEX index_name ON table(column(length))
–修改表結構的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
–刪除索引
DROP INDEX index_name ON table
唯一索引
索引列必須唯一,可以為空
創建唯一索引
CREATE UNIQUE INDEX indexName ON table(column(length))
–修改表結構
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
全文索引
MySQL從3.23.23版開始支持全文索引和全文檢索,FULLTEXT索引僅可用於 MyISAM 表;他們可以從CHAR、VARCHAR或TEXT列中作為CREATE TABLE語句的一部分被創建,或是隨后使用ALTER TABLE 或CREATE INDEX被添加。////對於較大的數據集,將你的資料輸入一個沒有FULLTEXT索引的表中,然后創建索引,其速度比把資料輸入現有FULLTEXT索引的速度更為快。不過切記對於大容量的數據表,生成全文索引是一個非常消耗時間非常消耗硬盤空間的做法。
–修改表結構添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content)
–直接創建索引
CREATE FULLTEXT INDEX index_content ON article(content)
單列索引,多列索引
多個單列索引與單個多列索引的查詢效果不同,因為執行查詢時,MySQL只能使用一個索引,會從多個索引中選擇一個限制最為嚴格的索引
組合索引(最左索引),這個索引很有意思
平時用的SQL查詢語句一般都有比較多的限制條件,所以為了進一步榨取MySQL的效率,就要考慮建立組合索引。例如上表中針對title和time建立一個組合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。建立這樣的組合索引,其實是相當於分別建立了下面兩組組合索引:
–title,time
–title
為什么沒有time這樣的組合索引呢?這是因為MySQL組合索引“最左前綴”的結果。簡單的理解就是只從最左面的開始組合。並不是只要包含這兩列的查詢都會用到該組合索引,如下面的幾個SQL所示:
–使用到上面的索引
SELECT * FROM article WHREE title='測試' AND time=1234567890;
SELECT * FROM article WHREE utitle='測試';
–不使用上面的索引
SELECT * FROM article WHREE time=1234567890;
索引怎么使用
1. 何時使用聚集索引或非聚集索引?
動作描述 使用聚集索引 使用非聚集索引
列經常被分組排序 使用 使用
返回某范圍內的數據 使用 不使用
一個或極少不同值 不使用 不使用
小數目的不同值 使用 不使用
大數目的不同值 不使用 使用
頻繁更新的列 不使用 使用
外鍵列 使用 使用
主鍵列 使用 使用
頻繁修改索引列 不使用 使用
事實上,我們可以通過前面聚集索引和非聚集索引的定義的例子來理解上表。如:返回某范圍內的數據一項。比如您的某個表有一個時間列,恰好您把聚合索引建立在了該列,這時您查詢2004年1月1日至2004年10月1日之間的全部數據時,這個速度就將是很快的,因為您的這本字典正文是按日期進行排序的,聚類索引只需要找到要檢索的所有數據中的開頭和結尾數據即可;而不像非聚集索引,必須先查到目錄中查到每一項數據對應的頁碼,然后再根據頁碼查到具體內容。其實這個具體用法我還不是很理解,只能等待后期的項目開發中慢慢學學了。
2. 索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
3. 使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
4. 索引列排序
MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。
5. like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
6. 不要在列上進行運算
例如:select * from users where YEAR(adddate)<2007,將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成:select * from users where adddate<’2007-01-01′。關於這一點可以圍觀:一個單引號引發的MYSQL性能損失。
小結
最后總結一下,MySQL只對一下操作符才使用索引:<,<=,=,>,>=,between,in,以及某些時候的like(不以通配符%或_開頭的情形)。而理論上每張表里面最多可創建16個索引,不過除非是數據量真的很多,否則過多的使用索引也不是那么好玩的,比如我剛才針對text類型的字段創建索引的時候,系統差點就卡死了。
索引的優缺點
為什么要創建索引呢?
這是因為,創建索引可以大大提高系統的性能。
第一、通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。
第二、可以大大加快 數據的檢索速度,這也是創建索引的最主要的原因。
第三、可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。
第四、在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。
第五、通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。
也許會有人要問:增加索引有如此多的優點,為什么不對表中的每一個列創建一個索引呢?這種想法固然有其合理性,然而也有其片面性。雖然,索引有許多優點, 但是,為表中的每一個列都增加索引,是非常不明智的。
這是因為,增加索引也有許多不利的一個方面:
第一、創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。
第二、索引需要占物理空間,除了數據表占數據空間之外,每一個索引還要占一定的物理空間。如果要建立聚簇索引,那么需要的空間就會更大。
第三、當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。
什么樣的字段適合創建索引:
索引是建立在數據庫表中的某些列的上面。因此,在創建索引的時候,應該仔細考慮在哪些列上可以創建索引,在哪些列上不能創建索引。
一般來說,應該在這些列上創建索引,例如:
第一、在經常需要搜索的列上,可以加快搜索的速度;
第二、在作為主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;
第三、在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;
第四、在經常需要根據范圍進行搜索的列上創建索引,因為索引已經排序,其指定的范圍是連續的;
第五、在經常需要排序的列上創建索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;
第六、在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度。
建立索引,一般按照select的where條件來建立,比如: select的條件是where f1 and f2,那么如果我們在字段f1或字段f2上簡歷索引是沒有用的,只有在字段f1和f2上同時建立索引才有用等。
什么樣的字段不適合創建索引:
同樣,對於有些列不應該創建索引。一般來說,不應該創建索引的的這些列具有下列特點:
第一,對於那些在查詢中很少使用或者參考的列不應該創建索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,
並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
第二,對於那些只有很少數據值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,
在查詢的結果中,結果集的數據行占了表中數據行的很大比 例,即需要在表中搜索的數據行的比例很大。
增加索引,並不能明顯加快檢索速度。
第三,對於那些定義為text, image和bit數據類型的列不應該增加索引。這是因為,這些列的數據量要么相當大,要么取值很少。
第四,當修改性能遠遠大於檢索性能時,不應該創建索 引。這是因為,修改性能和檢索性能是互相矛盾的。
當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。
因此,當修改性能遠遠大於檢索性能時,不應該創建索引。
創建索引的方法::
1、創建索引,例如 create index <索引的名字> on table_name (列的列表);
2、修改表,例如 alter table table_name add index[索引的名字] (列的列表);
3、創建表的時候指定索引,例如create table table_name ( [...], INDEX [索引的名字] (列的列表) );
查看表中索引的方法:
show index from table_name; 查看索引
索引的類型及創建例子::
1.PRIMARY KEY (主鍵索引)
MySQL> alter table table_name add primary key ( `column` )
2.UNIQUE 或 UNIQUE KEY (唯一索引)
mysql> alter table table_name add unique (`column`)
3.FULLTEXT (全文索引)
mysql> alter table table_name add fulltext (`column` )
4.INDEX (普通索引)
mysql> alter table table_name add index index_name ( `column` )
5.多列索引 (聚簇索引)
mysql> alter table `table_name` add index index_name ( `column1`, `column2`, `column3` )