一 、引言html
首先咱們來思考一下什麼是索引?索引的做用是什麼?操做系統的文件索引和數據庫的索引有什麼不一樣?mysql
什麼是索引?對於這個問題咱們能夠打一個比喻,索引相對於文件的做用,就比如是目錄相對於一本書的做用。因此它的做用也就顯而易見了,就是爲了查找,提升查找效率。是否是感受不太有用,那再想想你查字典的時候一頁一頁的找試一試,買一本最便宜的字典都要含着淚才能翻完。正常查找字典咱們通常先找到部首的筆畫,而後找到部首,再根據部首找到字,再根據字找到對應的頁,這其實就是一個多級索引。因此說計算機科學裏面的不少智慧來來自於生活。算法
接下來就是操做系統的文件索引和數據庫的索引的區別了。通常操做系統都有一張索引表,由於通常操做系統的文件是無結構的字節系列,因此操做系統的索引表記錄的是數據的邏輯塊號和對應的物理塊號。而數據庫文件是有結構的記錄,因此它能夠由每一條記錄的關鍵碼來和物理塊對應。sql
特別須要注意的是索引鍵對於的值是磁盤(外存)的物理地址,而不是內存中的邏輯地址。數據庫在讀取表的時候首先是先讀取索引文件(可能數據文件自己就是索引文件,這和不一樣的實現方式相關,InnoDB就是這種實現)。而後根據索引表來讀取數據。
數據庫
二 、選擇率性能
要理解利用索引對數據庫查詢作優化有一點很是重要,就是全表掃描和索引掃描的區別,索引下面的內容很是重要。優化
對數據庫操做影響最大的就是IO操做。表的掃描操做就和IO密切相關。由於數據庫通常都會經過操做系統的IO,操做系統中讀取數據用兩種很是重要方式:ui
順序讀取:就是讀取磁盤上連續的塊,速度快spa
隨機讀取:隨機讀是指訪問的塊是不連續的,須要磁盤的磁頭不斷移動。隨機讀的性能是遠遠低於順序讀的。操作系統
矛盾的問題來了,索引掃描是隨機讀取的,而全表掃描是順序讀取的。這明顯不科學啊!明明咱們利用索引是來作優化的。爲何用慢的隨機讀取呢?一個很重要的數據就是選擇率,瞭解它你就會發現不少其餘書籍或者文章中提到的利用索引優化的限制和技巧的緣由了。
常常看見提醒要作有where條件上的列加上索引,爲何呢?where就意味着過濾,不少時候過濾意味着咱們其實須要查詢的數據量很小,例如登陸時候的用戶驗證:
select 1 from user where username=’tom’ and password=sha1(’123456’)
咱們只須要查找一條記錄,若是咱們有1億條記錄,若是作全表掃描按一半來算平均也要掃描5千萬條記錄。這須要多少次IO操做啊?而索引掃描只須要讀取一條記錄,基本上一次IO就能夠搞定。因此當選擇率很低的時候,就算是用索引的隨機讀取比順序讀取慢不少,可是也會比全表掃描效率高不少。
還有一些優化技巧提醒你在取值範圍小的列上不要創建索引,也是由於選擇率的問題。例如在性別字段上創建一個索引,而後讀取數據:
select * from user where sex=’male’;
咱們按男女比例均衡來算,至少也要選擇通常的數據吧,就算加上一些特殊人羣,也是幾分之一的機率吧,若是數據量大,還不是就哭了。因此數據庫的優化器會在執行查詢計劃的時候就會計算選擇率,若是選擇率太高,就直接放棄索引掃描,改用全表掃描。由於咱們前面提過全表掃描用的順序讀取比索引掃描用的隨機讀取速度快。明顯選擇率高的時候咱們讀的數據多,用順序讀取的優點要大於用隨機讀取。
三 、Hash索引和B+數索引
Hash索引相對比較簡單,就是利用hash表來記錄列的值和對應列存儲的物理地址。它的效率和hash算法相關。
B+樹的B表明的是Balance(平衡)而不是Binary(二叉),用B+樹實現的 索引也不能定位到數據項,只能定位到頁(見注1),這和B+樹特色和實現相關,B+樹也是樹,也有節點,而MySQL數據庫把一個節點的大小設置爲一頁,這也一次就能夠讀取一個節點。它有這很好的查詢效率比O(n)好。
關於hash索引和B+樹索引,若是你感興趣能夠參考後面給出的參考列表,這裏就不作詳細介紹了,下圖是我截取的一張爲何有hash索引還要B+樹索引的緣由,由於認爲可能你會用到,可是爲了防止鏈接失效,因此截取了一張圖。後面有原文鏈接,若是你感興趣能夠參考原文。
圖1:爲何還須要B+樹索引
四 、實例
如今仍是不太明顯可以感覺到索引對於數據庫效率的影響是嗎?不要緊,咱們用一個實例來分析一下使用索引和不使用索引的區別,而後對比一下使用索引的性能和不使用索引的性能差別。
假設咱們有一張user表以下所示:
圖2:user表
user表的建立語句:
CREATE TABLE IF NOT EXISTS `user` ( `id` INT NOT NULL , `name` CHAR(30) NOT NULL , `phone` CHAR(11) NULL , `address` VARCHAR(50) NULL , `password` CHAR(40) NOT NULL , `description` TEXT NULL , PRIMARY KEY (`id`) )ENGINE = InnoDB;
user表只有一個主鍵索引,是數據庫默認爲主鍵加上的。咱們理一下如今當咱們想user表插入插入數據和查詢數據時數據庫是怎麼作的。
插入一條記錄時,數據庫首先根據索引列id生成一個值做爲索引的鍵值,把記錄存放的物理地址做存放在相應的表記錄中。索引表與數據存儲在物理存儲上的關係以下圖2。
圖3:數據庫索引使用示意圖
這也體現了索引的負效應,就是要維護索引表,當插入和刪除記錄的時候,要跟新索引表,這就帶來了消耗。注意圖2只是一個示意圖,索引的結構並不必定是這樣。
咱們在來看查詢數據,當咱們只查詢索引列的時候,就會使用索引掃描。不然使用順序掃描讀取。在MySQL中咱們能夠用explain語句來驗證一下:
圖4:只查詢索引
圖5:只查詢非索引列
圖6:同時查詢索引列和非索引列
咱們看到了只有當咱們查詢索引列的時候才使用了索引,爲何都是一條記錄,可是隻有索引列才使用索引呢?這樣和前文提到的順序讀取和隨機讀取的效率有關。
最後,若是能夠在表中插入幾百萬條數據,而後來驗證一下一樣選擇一行數據,利用索引掃描和利用全表掃描的效率差異。
五 、相關的參考
操做系統的IO管理介紹:http://www.94cto.com/index/Article/content/id/748.html
磁盤IO參數相關:http://storage.it168.com/a2011/0323/1169/000001169755.shtml
B+樹相關:http://blog.sina.com.cn/s/blog_4e0c21cc01010itp.html
Hash索引和B+樹索引:
http://dev.mysql.com/doc/refman/5.6/en/index-btree-hash.html(官方文檔)
http://blog.sina.com.cn/s/blog_6776884e0100pko1.html
操做系統分頁相關:http://blog.chinaunix.net/uid-28458801-id-3505434.html
操做系統文件讀取:http://blog.csdn.net/hguisu/article/details/6120991
理解B+樹算法和Innodb索引:http://www.ruzuojun.com/topic/420.html
注1:這裏的頁是指操做系統的頁面大小,爲何要分頁呢?簡單的說就是咱們的物理內存是有限的,咱們要利用虛擬內存(磁盤等外存)。當咱們的物理內存不夠用的時候,就會把物理內存中的一些數據置換到虛擬內存中,可是置換多大的內存呢?爲了方便管理,操做系統一般作法就是對內存就行分頁,按頁置換。讀取數據時,一個塊的大小通常也是一個頁的大小。