SQL索引在數據庫優化中佔有一個很是大的比例, 一個好的索引的設計,可讓你的效率提升幾十甚至幾百倍,在這裏將帶你一步步揭開他的神祕面紗。mysql
1.1 什麼是索引?web
SQL索引有兩種,彙集索引和非彙集索引,索引主要目的是提升了SQL Server系統的性能,加快數據的查詢速度與減小系統的響應時間 sql
下面舉兩個簡單的例子:數據庫
圖書館的例子:一個圖書館那麼多書,怎麼管理呢?創建一個字母開頭的目錄,例如:a開頭的書,在第一排,b開頭的在第二排,這樣在找什麼書就好說了,這個就是一個彙集索引,但是不少人借書找某某做者的,不知道書名怎麼辦?圖書管理員在寫一個目錄,某某做者的書分別在第幾排,第幾排,這就是一個非彙集索引api
字典的例子:字典前面的目錄,能夠按照拼音和部首去查詢,咱們想查詢一個字,只須要根據拼音或者部首去查詢,就能夠快速的定位到這個漢字了,這個就是索引的好處,拼音查詢法就是彙集索引,部首查詢就是一個非彙集索引.性能優化
看了上面的例子,下面的一句話你們就很容易理解了:網絡
1.2 索引的存儲機制app
首先,無索引的表,查詢時,是按照順序存續的方法掃描每一個記錄來查找符合條件的記錄,這樣效率十分低下,舉個例子,若是咱們將字典的漢字隨即打亂,沒有前面的按照拼音或者部首查詢,那麼咱們想找一個字,按照順序的方式去一頁頁的找,這樣效率有多底,你們能夠想象。數據庫設計
彙集索引和非彙集索引的根本區別是表記錄的排列順序和與索引的排列順序是否一致,其實理解起來很是簡單,仍是舉字典的例子:若是按照拼音查詢,那麼都是從a-z的,是具備連續性的,a後面就是b,b後面就是c, 彙集索引就是這樣的,他是和表的物理排列順序是同樣的,例若有id爲彙集索引,那麼1後面確定是2,2後面確定是3,因此說這樣的搜索順序的就是彙集索引。ide
非彙集索引就和按照部首查詢是同樣是,可能按照偏房查詢的時候,根據偏旁‘弓’字旁,索引出兩個漢字,張和弘,可是這兩個其實一個在100頁,一個在1000頁,(這裏只是舉個例子),他們的索引順序和數據庫表的排列順序是不同的,這個樣的就是非彙集索引。
原理明白了,那他們是怎麼存儲的呢?在這裏簡單的說一下,彙集索引就是在數據庫被開闢一個物理空間存放他的排列的值,例如1-100,因此當插入數據時,他會從新排列整個整個物理空間,而非彙集索引其實能夠看做是一個含有彙集索引的表,他只僅包含原表中非彙集索引的列和指向實際物理表的指針。他只記錄一個指針,其實就有點和堆棧差很少的感受了
1.3 什麼狀況下設置索引
1) 定義主鍵的數據列必定要創建索引。
2) 定義有外鍵的數據列必定要創建索引。
3) 對於常常查詢的數據列最好創建索引。
4) 對於須要在指定範圍內的快速或頻繁查詢的數據列;
5) 常常用在WHERE子句中的數據列。
6) 常常出如今關鍵字order by、group by、distinct後面的字段,創建索引。若是創建的是複合索引,索引的字段順序要和這些關鍵字後面的字段順序一致,不然索引不會被使用。
7) 對於那些查詢中不多涉及的列,重複值比較多的列不要創建索引。
8) 對於定義爲text、image和bit的數據類型的列不要創建索引。
9) 對於常常存取的列避免創建索引
9) 限制表上的索引數目。對一個存在大量更新操做的表,所建索引的數目通常不要超過3個,最多不要超過5個。索引雖然說提升了訪問速度,但太多索引會影響數據的更新操做。
10) 對複合索引,按照字段在查詢條件中出現的頻度創建索引。在複合索引中,記錄首先按照第一個字段排序。對於在第一個字段上取值相同的記錄,系統再按照第二個字段的取值排序,以此類推。所以只有複合索引的第一個字段出如今查詢條件中,該索引纔可能被使用,所以將應用頻度高的字段,放置在複合索引的前面,會使系統最大可能地使用此索引,發揮索引的做用。
上面都在說使用索引的好處,但過多的使用索引將會形成濫用。所以索引也會有它的缺點:
使用索引時,有如下一些技巧和注意事項:
只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲NULL。
對串列進行索引,若是可能應該指定一個前綴長度。例如,若是有一個CHAR(255)的列,若是在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不只能夠提升查詢速度並且能夠節省磁盤空間和I/O操做。
MySQL查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。
通常狀況下不鼓勵使用like操做,若是非使用不可,如何使用也是一個問題。like 「%aaa%」 不會使用索引,而like 「aaa%」可使用索引。
將在每一個行上進行運算,這將致使索引失效而進行全表掃描,所以咱們能夠改爲
1.4 如何建立索引
1.41 建立索引的語法:
1.42 刪除索引語法:
1.43 顯示索引信息:
使用系統存儲過程:sp_helpindex 查看指定表的索引信息。
執行代碼以下:
1.44查詢索引(都可)
1.44組合索引
不少時候,咱們在mysql中建立了索引,可是某些查詢仍是很慢,根本就沒有使用到索引!通常來講,多是某些字段沒有建立索引,或者是組合索引中字段的順序與查詢語句中字段的順序不符。
看下面的例子:
假設有一張訂單表(orders),包含order_id和product_id二個字段。
一共有31條數據。符合下面語句的數據有5條。執行下面的sql語句:
這條語句要mysql去根據order_id進行搜索,而後返回匹配記錄中的product_id。因此組合索引應該按照如下的順序建立:
爲何要建立組合索引呢?這麼簡單的狀況直接建立一個order_id的索引不就好了嗎?果只有一個order_id索引,沒什麼問題,會用到這個索引,而後mysql要去磁盤上的表裏面取到product_id。若是有組合索引的話,mysql能夠徹底從索引中取到product_id,速度天然會快。再多說幾句組合索引的最左優先原則:
組合索引的第一個字段必須出如今查詢組句中,這個索引纔會被用到。果有一個組合索引(col_a,col_b,col_c),下面的狀況都會用到這個索引:
經過實例理解單列索引、多列索引以及最左前綴原則。實例:如今咱們想查出知足如下條件的用戶id:
由於咱們不想掃描整表,故考慮用索引。
1.單列索引:
將lname列建索引,這樣就把範圍限制在lname='Liu'的結果集1上,以後掃描結果集1,產生知足fname='Zhiqun'的結果集2,再掃描結果集2,找到 age=26的結果集3,即最終結果。
由 於創建了lname列的索引,與執行表的徹底掃描相比,效率提升了不少,但咱們要求掃描的記錄數量仍舊遠遠超過了實際所需 要的。雖然咱們能夠刪除lname列上的索引,再建立fname或者age 列的索引,可是,不論在哪一個列上建立索引搜索效率仍舊類似。
2.多列索引:
爲了提升搜索效率,咱們須要考慮運用多列索引,因爲索引文件以B-Tree格式保存,因此咱們不用掃描任何記錄,便可獲得最終結果。
注:在mysql中執行查詢時,只能使用一個索引,若是咱們在lname,fname,age上分別建索引,執行查詢時,只能使用一個索引,mysql會選擇一個最嚴格(得到結果集記錄數最少)的索引。
3.最左前綴:顧名思義,就是最左優先,上例中咱們建立了lname_fname_age多列索引,至關於建立了(lname)單列索引,(lname,fname)組合索引以及(lname,fname,age)組合索引。
注:在建立多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
創建索引的時機
到這裏咱們已經學會了創建索引,那麼咱們須要在什麼狀況下創建索引呢?通常來講,在WHERE和JOIN中出現的列須要創建索引,但也不徹底如此,由於MySQL只對<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE纔會使用索引。例如:
此時就須要對city和age創建索引,因爲mytable表的userame也出如今了JOIN子句中,也有對它創建索引的必要。
剛纔提到只有某些時候的LIKE才需創建索引。由於在以通配符%和_開頭做查詢時,MySQL不會使用索引。例以下句會使用索引:
下句就不會使用:
所以,在使用LIKE時應注意以上的區別。
1.5 索引實戰(摘抄)
人們在使用SQL時每每會陷入一個誤區,即太關注於所得的結果是否正確,而忽略了不一樣的實現方法之間可能存在的性能差別,
這種性能差別在大型的或是複雜的數據庫環境中(如聯機事務處理OLTP或決策支持系統DSS)中表現得尤其明顯。
筆者在工做實踐中發現,不良的SQL每每來自於不恰當的索引設計、不充份的鏈接條件和不可優化的where子句。
在對它們進行適當的優化後,其運行速度有了明顯地提升!
下面我將從這三個方面分別進行總結:
爲了更直觀地說明問題,全部實例中的SQL運行時間均通過測試,不超過1秒的均表示爲(< 1秒)。
1、不合理的索引設計----
例:表record有620000行,試看在不一樣的索引下,下面幾個 SQL的運行狀況:
---- 1.在date上建有一非個羣集索引
---- 2.在date上的一個羣集索引
---- 3.在place,date,amount上的組合索引
---- 4.在date,place,amount上的組合索引
---- 5.總結:----
缺省狀況下創建的索引是非羣集索引,但有時它並非最佳的;合理的索引設計要創建在對各類查詢的分析和預測上。
通常來講:
①.有大量重複值、且常常有範圍查詢(between, >,< ,>=,< =)和order by、group by發生的列,可考慮創建羣集索引;
②.常常同時存取多列,且每列都含有重複值可考慮創建組合索引;
③.組合索引要儘可能使關鍵查詢造成索引覆蓋,其前導列必定是使用最頻繁的列。
2、不充份的鏈接條件:
例:表card有7896行,在card_no上有一個非彙集索引,表account有191122行,在account_no上有一個非彙集索引,試看在不一樣的錶鏈接條件下,兩個SQL的執行狀況:
---- 分析:----
可見,只有充份的鏈接條件,真正的最佳方案纔會被執行。
總結:
1.多表操做在被實際執行前,查詢優化器會根據鏈接條件,列出幾組可能的鏈接方案並從中找出系統開銷最小的最佳方案。鏈接條件要充份考慮帶有索引的表、行數多的表;內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次查找的次數肯定,乘積最小爲最佳方案。
2.查看執行方案的方法-- 用set showplanon,打開showplan選項,就能夠看到鏈接順序、使用何種索引的信息;想看更詳細的信息,需用sa角色執行dbcc(3604,310,302)。
3、不可優化的where子句
1.例:下列SQL條件語句中的列都建有恰當的索引,但執行速度卻很是慢:
你會發現SQL明顯快起來!
2.例:表stuff有200000行,id_no上有非羣集索引,請看下面這個SQL:
---- 總結:----
可見,所謂優化即where子句利用了索引,不可優化即發生了表掃描或額外開銷。
1.任何對列的操做都將致使表掃描,它包括數據庫函數、計算表達式等等,查詢時要儘量將操做移至等號右邊。
2.in、or子句常會使用工做表,使索引失效;若是不產生大量重複值,能夠考慮把子句拆開;拆開的子句中應該包含索引。
3.要善於使用存儲過程,它使SQL變得更加靈活和高效。
從以上這些例子能夠看出,SQL優化的實質就是在結果正確的前提下,用優化器能夠識別的語句,充份利用索引,減小表掃描的I/O次數,儘可能避免表搜索的發生。其實SQL的性能優化是一個複雜的過程,上述這些只是在應用層次的一種體現,深刻研究還會涉及數據庫層的資源配置、網絡層的流量控制以及操做系統層的整體設計。