原文:https://blog.csdn.net/sundacheng1989/article/details/53117172
https://www.cnblogs.com/gavinsp/p/5513536.html
https://www.cnblogs.com/lpshou/p/3364282.html
最近使用到Oracle數(shù)據(jù)庫的索引比較多,所以就想好好研究一下索引到底是什么。畢竟作為一個Application Developer,而不是DBA,所以這篇文字也是很通俗,特別淺顯的描述了一下索引相關(guān)的概念。
為什么需要索引?數(shù)據(jù)在磁盤上是以塊的形式存儲的。為確保對磁盤操作的原子性,訪問數(shù)據(jù)的時候會一并訪問所有數(shù)據(jù)塊。磁盤上的這些數(shù)據(jù)塊與鏈表類似,即它們都包含一個數(shù)據(jù)段和一個指針,指針指向下一個節(jié)點(數(shù)據(jù)塊)的內(nèi)存地址,而且它們都不需要連續(xù)存儲(即邏輯上相鄰的數(shù)據(jù)塊在物理上可以相隔很遠)。
舉個例子來講,我們有一個數(shù)據(jù)表User.為了簡便,這個表沒有主鍵。
Identity | Name | Age | Grade |
1 | Robin | 28 | 90 |
5 | Lilei | 26 | 60 |
3 | Hanmei | 25 | 50 |
4 | Lucy | 27 | 66 |
2 | Lily | 29 | 80 |
雖然這些數(shù)據(jù)都存在于一個User表中,但是物理上,這些數(shù)據(jù)可能存儲在分散的數(shù)據(jù)塊中。
查找Lily這個人的信息, 已知Lily的Identity為2, select * fromUser where Identity= 2.
在查找的時候,首先找到這個表的第一條記錄所在的數(shù)據(jù)庫地址,然后發(fā)現(xiàn)Identity為1,并不是所需要的值,然后在這個數(shù)據(jù)庫的底端,找到了下一個數(shù)據(jù)塊的地址。(這個類似于鏈表),如此一來,查詢了5次才找到了所需要的值。(為了簡單起見,我們考慮Identity不能有重復(fù)值)
為了加快搜索速度,這里就出現(xiàn)了索引。索引是對某個字段進行排序的一種方式。對表中的某個字段建立索引會創(chuàng)建另一種數(shù)據(jù)結(jié)構(gòu),其中保存著字段的值,每個值又指向與它相關(guān)的記錄。這種索引的數(shù)據(jù)結(jié)構(gòu)是經(jīng)過排序的,因而可以對其執(zhí)行二分查找。
對上個表的Identity字段進行索引,就是在數(shù)據(jù)庫存儲空間上創(chuàng)建一塊專用的控件,把User表的所有的Identity字段的值拿出來放到這里,并且對這些值進行排序,并且每個值都攜帶著這個Identity對應(yīng)的行所在數(shù)據(jù)塊的地址。因為Identity是進過排序的,按照一定的數(shù)據(jù)結(jié)構(gòu)存儲的,所以數(shù)據(jù)庫引擎在查找的時候,比如說查找identity為5,引擎就會計算,5大概在整個排序結(jié)構(gòu)的大致地方,然后到那里去拿出這個值看看是不是,不是的話就再次相應(yīng)的向左或者向右移動去尋找。(這里用到的知識都是大學(xué)時候的數(shù)據(jù)結(jié)構(gòu)的知識,二分法查找,相對于毫無頭緒的一個一個的查找,二分法的查找速度明顯的提高,達到了log2 N,其實這有多快我也不明白,反正就記得當時學(xué)的時候,確實是比一般查找快多了。)
通俗的來講,就是根據(jù)你指定的列,建立一個遵循一定數(shù)據(jù)結(jié)構(gòu)的區(qū)域,這些區(qū)域可以快速定位到相應(yīng)數(shù)據(jù)庫字段所在的磁盤地址。
索引的好處是特別明顯的,那就是大大的提高了查詢的速度。但是相對應(yīng)的也帶來了一些不好的地方。
第一,創(chuàng)建索引和維護索引要耗費時間,這種時間隨著數(shù)據(jù)量的增加而增加。
第二,索引需要占物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間就會更大。
第三,當對表中的數(shù)據(jù)進行增加、刪除和修改的時候,索引也要動態(tài)的維護,這樣就降低了數(shù)據(jù)的維護速度。
最后還有一點需要注意的是,我們在數(shù)據(jù)庫上對于某個字段建立了索引,那么什么情況下才走索引呢?
比如 select * from User where Identity= 2 這條語句,是走索引查詢的。因為是否走索引取決于這條查詢語句的where子句。數(shù)據(jù)庫引擎發(fā)現(xiàn)你的where語句中有identity,那么就會從identity的索引數(shù)據(jù)結(jié)構(gòu)中進行檢索。曾經(jīng)看到有人說select *會降低檢索速度,這個跟索引沒關(guān)系,select * 降低檢索速度,是因為從數(shù)據(jù)庫服務(wù)器端到客戶端的網(wǎng)絡(luò)傳輸是有時間的,select * 中難免包含著不必要的字段,所以傳輸起來會比較慢。
接下來單純的比較一下select * 與select 單個字段在速度上的區(qū)別。如果數(shù)據(jù)量非常非常大的話,這種速度上的差別是非常明顯的。下邊這個例子,是從相同的數(shù)據(jù)庫表中去拿數(shù)據(jù)。
當只是返回一個OUT_ID字段的時候,你可以看到49秒鐘的時候就處理了30萬條數(shù)據(jù)。
這時候我們使用select * 這種方式,我們發(fā)現(xiàn),在用事一分鐘的時候,才處理了3萬條數(shù)據(jù)。
從上邊的對比中我們可以看出,在數(shù)據(jù)量非常大而且數(shù)據(jù)表字段非常多的時候,這兩種方式在檢索時間上的差別還是非常大的。
---------------------------------------------------------------------------------------------------------------------------------------