索引是什麼?相信你們都用過字典。你是怎麼從厚厚的新華字典中找到你須要找到的那個字的呢?又是怎麼從一本書中快速定位到你須要的章節?
咱們都是經過書中的目錄,而後根據目錄中的頁碼定位到咱們要的信息。mysql
一樣在mysql中也是這樣爲咱們準備了一份目錄。當你去經過sql語句查詢的時候用不用索引,以及怎麼用索引。決定了你的查詢所耗費的時間。算法
在優化咱們的應用的時候,首先應該考慮的是使用索引,試圖經過其它途徑來提升性能則純粹是在浪費時間。你應該先使用索引最大程度的改進性能,再考慮看看是否有其它的技術可使用。sql
因此,索引是什麼?—— 是一份通過排序的目錄表。數據庫
沒有索引的數據表:數組
一個沒有索引的數據表,就是一個無序的數據行的集合。咱們要從中找到符合條件的一行記錄,須要掃描整個表,挨個詢問,你是否是?是,留下。不是,找下一個。固然了,經過sql查詢出來的數據行也是一個集合。網絡
存在索引的數據表:函數
先看一張數據表工具
當咱們給Id
添加索引的時候生成的索引文件多是這個樣子的性能
存在索引的數據表,在查詢過程當中就不須要再去掃描整張表中的數據行了。好比咱們給Id
添加了一個索引,當咱們要查詢的條件是:Id=13
的時候,咱們開始掃描索引,並找到了3條符合條件的記錄行。當掃描到達了Id爲14的數據行的時候,這個值高於咱們搜索的值。因爲索引是通過分類的,因此當讀取到包含14的記錄時,咱們就知道再也不會有與Id=13
相吻合的數據,從而再也不進行掃描。測試
在上面查詢索引文件的時候,使用的是線性掃描,(從第一個,到認爲以後不符合條件的最後一個)。另外一種方式是定位算法的使用,它們能夠不通過線性掃描就能夠直接定位到第一個匹配項。從而節省了大量的搜索時間。各類數據庫使用各類各樣的技術來迅速的找到索引值。而咱們只須要知道的是索引這個工具用來幹啥,怎麼用就好了。
在上面的例子中,能夠看出不使用索引跟使用索引進行查詢時它們的差異。若是還不能給你震撼感,那麼舉個例子:
假設你有三張沒有索引的數據表:t一、t二、t3。每張表有1000條數據行。咱們要找出這三張表中具備相同數值的全部數據行的組合: SELECT t1.i1,t2.i2,t3.i3 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.i1 = t2.i2 AND t2.i2 = t3.i3; 這個查詢的結果應該有1000個數據行,每行都包含3個相等的數據行。當咱們不使用索引來查詢的時候,t1表的每行記錄咱們都須要拿到 t2表中進行全表掃描,一樣t2表中符合的記錄,咱們須要拿到t3表進行全表掃描。 這樣子獲得符合的記錄可能的組合是:1000x1000x1000(10億)種,比匹配的數目多100萬倍。
爲數據表編制索引能夠很大程度的提升查詢的速度,它能夠像下面這樣處理查詢: 1. 從數據表t1中選擇第一個數據行,看這個數據行包含什麼樣的值。 2. 對數據表t2使用索引,直接找到與t1表中相匹配的數據行,相似的對數據表t3使用索引,直接找到與t1表相匹配的數據行 3. 對數據表t1的下一個數據行重複上面的過程,直到檢查完t1表中全部的數據行。 使用索引後對t1表仍是進行了全表掃描,可是可以對t二、t3數據錶帶索引搜尋。直接將那些數據行挑選出來,這種查詢運行速度比不帶索引的查詢運行快100萬倍。
在查詢操做中把與WHERE
子句所給出的條件相匹配的數據行儘快找出來
在關聯操做中把與其它數據表裏的相匹配的數據行儘快找出來
對於使用MIN()
或MAX()
這些聚合函數,若是數據列帶索引,那麼它的最小值和最大值可以快速被找到,而不須要進行全表掃描
mysql常用索引完成ORDER BY
和GROUP BY
子句的分類和分組操做。
有時候mysql能夠經過使用索引避免一個查詢掃描完索引文件再去讀取數據行。假如你是從MyISam
數據表的一個有索引的數據列裏選取值,而你並不打算讀取其它數據列。在這種狀況下Mysql從這個索引文件讀取索引值時,你實際上已經獲取到了這個值。而這個值你原本是應該經過讀取數據行才能獲得。在這你就不須要讀取兩次值了。這也是咱們爲何不建議寫SELECT * FROM tableName
這種語句。第一,若是僅僅須要獲取一個字段值,而且這個值有索引,好比用戶的Id
主鍵索引,就不須要查詢兩次表,直接獲取就好了。第二個,對於沒必要要的字段,好比文章內容這種佔用空間比較大的字段會佔用較大的網絡帶寬。
對於不一樣的存儲引擎,索引實現的細節有所不一樣。對與MyISAM
數據表來講,數據表的數據行是在數據文件裏。而索引值是在索引文件裏。一個數據表能夠有多個索引,全部的索引都存儲在同一個索引文件裏。索引文件裏的每一個索引都是由分類的關鍵記錄數組組成的。這些數組用於快速訪問數據表文件。
InnoDB
存儲引擎使用的是一個表空間,在這個表空間裏,它管理着全部的InnoDB
類型的數據表的數據和索引的存儲。咱們也能夠經過配置使每一個使用InnoDB
引擎的數據表建立本身的一個表空間。
談到索引的缺點,仍是回到剛開始咱們介紹什麼是索引的時候舉的例子。咱們有一本書,書中有目錄和內容。當咱們要往這本書裏面新增一篇文章的時候步驟是這樣的:
找到這篇文章須要添加的位置,插入進去。
更新這本書的目錄,使讀者能夠快速的定位到這篇文章。
此時你會發現一個問題,當你新增的文章愈來愈多的時候,你的目錄也會變得愈來愈厚。對了~索引會佔用必定的磁盤空間。
另外的是,每次新增一篇文章你都得更新下目錄。會佔用必定的時間。索引也同樣,因此當涉及到對有索引的數據表進行插入、刪除、更新,等操做的時候,索引會下降這些操做的性能。之因此會出現這種狀況是因爲對一條數據行進行插入操做,不只要修改數據表中的數據行,還要求所修改的數據行的索引要作出改變。一個數據表有越多的索引,須要作出的改變就越多,平均性能降低就越多。
絕大多數表都是讀操做多過於寫操做,但對那些寫操做次數比較多的表來講,索引更新的開銷可能會很是大。
對於MyISAM
數據表來講,大量的索引一個數據表有多是索引文件比數據文件更快的達到它的最大尺寸。
存儲在InnoDB
共享表空間裏的所有InnoDB
數據表分享一個存儲空間。添加索引會使表空間裏用於存儲的空間更快的減小。和MyISAM
數據表不一樣的是InnoDB
數據表的共享表空間不受操做系統文件尺寸的限制。若是配置成每一個使用InnoDB
引擎的數據表使用本身的表空間,數據和索引保存在一個文件裏,增長索引將會致使數據表的尺寸更快的逼近文件的最大長度。
儘管有上面所提到的時間和空間上的缺點,可是你見過一本書沒有目錄嗎?因此在使用索引的時候須要均衡的考慮,是否是非用不可。能夠多進行幾回基準測試,固然了,若是時間容許的話。
儘可能爲用來搜索、分類或分組的數據列編制索引
你有一張數據表,該怎麼具體爲哪個字段添加索引,可以讓mysql的優化器找到它,並使用它?
儘可能爲用來搜索、分類或分組的數據列編制索引。不要爲做爲顯式或輸出的數據列編制索引。也就是說最適合有索引的數據列 是那些在: 1. 出如今 WHERE 子句中的數據列 2. 在聯結子句中出現的數據列 如:SELECT * FROM aTable INNER JOIN bTable ON aTable.Id = bTable.Id; 3. 在 ORDER BY 或 GROUP BY 子句中出現的數據列。 根據 SELECT 子句中出現的數據列僅僅用來輸出顯式的字段最好不要有索引。
考慮數據列的維度
數據列的維度等於它所容納的非重複值的個數。好比說有個數據列裏面的值分別是:1,5,19,75,5,1
。它的維度就是 4
(去掉重複值後)。數據列的維度的最大值等於表裏數據行的個數。數據列的維度值越高包含的重複值就越少,索引的使用效果也就越好。
之前在設計表的時候我也糾結過到底要不要給數據表中的 Status(狀態)
加索引。由於這個字段是常常在WHERE
語句中出現的。而實際上這種能夠用ENUM
數據類型的字段當他在數據表中的數據行出現頻率超過30%
後mysql的查詢優化器一般會跳過索引,而進行全表掃描。如今的優化器更復雜,可以把其它因素考慮進來。百分比再也不是mysql決定進行一次全表掃描而不使用索引的惟一依據了。因此當你認爲該數據列的維度確實不是太底的時候能夠用DESC
或者EXPLAIN
與驗證下優化器到底有沒有使用到索引。
關於複合索引
當你建立了一個n
個數據列的複合索引時,實際上就建立了mysql可以使用的n
個索引。怎麼說呢?好比:
KEY `indexName` (`column1`,`column2`,`column3`);
注意索引的順序,在查詢中可以使用的索引順序以下:
column1, column2, column3 column1, column2 column1
mysql不能使用沒有包含最左邊索引字段的索引(column1
)。好比:column2,column3
。猜測下若是使用column1,column3
呢?mysql 能不能用到索引?答案是能夠的,但也只用到了column1
這個索引,column3
這個索引時用不到的。也就是說mysql可以使用到column1
去縮小匹配的範圍,可是這個索引不能用於這個值的組合。