天天看點

為什麼索引可以讓查詢變快?終于有人說清楚了!

概述

人類存儲資訊的發展曆程大緻經曆如下:

為什麼索引可以讓查詢變快?終于有人說清楚了!

由于是個人憑着自己了解總結的,是以可能不一定精确,但是毋庸置疑的是,在當代,各大公司機構部門的資料都是維護在資料庫當中的。資料庫作為資料存儲媒體發展的最新産物,必然是具有許多優點的,其中一個很大的優點就是存儲在資料庫中的資料通路速度非常快。

資料庫通路速度快的一個很重要的原因就在于索引index的作用。也就是這篇文章的主要想介紹的内容,為什麼索引可以讓資料庫查詢變快?

計算機存儲原理

在了解索引這個概念之前,我們需要先了解一下計算機存儲方面的基本知識。

我們知道資料持久化之後存在了資料庫裡,那麼我現在的問題是資料庫将資料存在了哪裡?答案顯然是存在了計算機的儲存設備上。就個人電腦而言,資料被存在了我們的電腦儲存設備上。

計算機的儲存設備有很多種,其中速度越快的越貴,是以容量也往往越小例如我們的RAM随機存儲器,也就是大家平時說的記憶體條,速度慢的就相對便宜例如我們的硬碟。而我們的資料往往都是被存在最慢的儲存設備硬碟上的,因為存在當中的資料在斷電之後依然存在。

計算機的存儲媒體有多種,例如硬碟,例如告訴緩存,不同的存儲媒體的資料讀取速度是不一樣的。例如,像RAM這樣的易失性儲存設備的讀寫操作就非常快,通路其中的資料幾乎沒有延遲性。由于這個原因,計算機作業系統的設計是這樣的:資料永遠不會直接從硬碟等機械裝置中取出,而是首先從硬碟轉移到更快的儲存設備,例如RAM,從RAM當中應用程式直接按需擷取資料。

計算機内部的機械硬碟是下面這樣的:

為什麼索引可以讓查詢變快?終于有人說清楚了!

在一個典型的硬碟驅動器中可以有很多個盤片,“盤片”在外觀上非常類似于一個CD光牒(但具有很高的存儲容量)。盤片又被磁道分條,同時一個盤片又可以分為扇區。

要擷取資料,“盤片”需要由主軸進行旋轉。大多數硬碟供應商都提到了主軸旋轉的速度,例如,7200轉/分和15000轉/分。磁盤中的資料總是以扇區的固定大小倍數表示。是以,如果要從硬碟通路資料,需要執行以下步驟,這也是性能開銷的主要來源。

确定資料所在的正确磁道,并将磁頭移動到該磁道。即通常說的尋道。

讓“主軸”旋轉盤片,使正确的扇區位于“磁盤頭”下方。

從扇區開始到扇區結束擷取整個資料。

如果資料恰好分布在連續扇區上,那麼它将提高擷取資料的性能。因為主軸和磁頭本身不需要移動/旋轉,也就沒有太多開銷,但是大多數時候這種開銷是存在的。

由于存在這種開銷,我們不能直接從硬碟擷取資料。RAM的存儲器高性能的背後的主要原因是它沒有像硬碟那樣的機械運動部件。但是盡管RAM的性能很高,但它當中的資料卻不會用作永久存儲,斷電之後就會消失,重新啟動之後就什麼都沒有了,這是我們需要硬碟來進行持久化的原因所在。資料庫中的資料毫無疑問就是存放在硬碟當中的,是以通路資料庫中的資料不可避免的會經曆磁盤操作的開銷。

索引是如何工作的?

知道上述知識後,索引就更容易了解了。

舉個例子,想象一下,現在有一本500頁厚包含幾十萬字的字典,同時裡面的字是無序排列的,現在我需要你從中找出某幾個字出來同時不允許檢視目錄。毫無疑問,我們隻能一頁一頁的翻,這是非人類能接受的工作,我們必然想的是先看目錄,找到相關的字或者偏旁,然後去對應的地方查找文字,這樣效率就大大提高了。目錄事實上就是一種索引,其思想一脈相承。

資料庫的索引類似于書中的這個目錄。索引會幫助我們快速檢索資料庫,查詢不需要通過整個表來擷取資料,而是從索引中找到資料塊。以一張資料庫表為例:

為什麼索引可以讓查詢變快?終于有人說清楚了!

上表是一張真實的資料庫表,其中每一行是一條記錄,每條記錄都有字段。假設上面的資料庫是一個有10萬條記錄的大資料庫。現在,我們想從10萬條記錄中搜尋一些内容,那麼挨着一個一個搜尋無疑将花費很長的時間,這個時候我們在資料結構與算法裡學的二分查找法就派上了用場。

二分查找法

使用二分查找法,需要将資料先排序,但是其查詢效率将大大提高。例子如下:

假設我們在上面的資料庫中使用的是固定長度的記錄,固定塊記錄大小為205個位元組, 預設塊大小是1024位元組。則:

固定記錄大小=204位元組,塊大小=1024位元組      

是以每個資料塊的記錄數=1024/204=5條記錄,10萬條記錄就是2萬個塊

不使用任何算法,我們要查詢100000條記錄中的某一條,,在最壞的情況下我們需要周遊一遍2萬block才能獲得全部100000條記錄。但如果進行二分查找,則隻需要進行20000的對數基數2,即14.287712次即可。這意味着我們隻需對排序後的值進行14次搜尋,就可以使用二分查找到您感興趣的唯一值。

為什麼索引可以讓查詢變快?終于有人說清楚了!

上圖是對一串數字生成的二叉查找樹。其時間複雜度為O(n)=O(log2N),即以2為底,n的對數。其中n為查找目标群體的總資料量。

例如,假設N為8,則O(n) = O(2為底8的對數) = O(3).

周遊方式,其時間複雜度為O(n)

在上述例子當中,n就是10000。使用索引的時間複雜度為O(2為底10000的對數) 大約等于 13. 和O(10000)之間差大概800倍。

索引為何使得查詢變快?

這個時候我們就能直接回答上述問題了,建立了索引的資料,就是通過事先排好序,進而在查找時可以應用二分查找來提高查詢效率。這也解釋了為什麼索引應當盡可能的建立在主鍵這樣的字段上,因為主鍵必須是唯一的,根據這樣的字段生成的二叉查找樹的效率無疑是最高的。

為什麼索引不能建立的太多?

如果一個表中所有字段的索引很大,也會導緻性能下降。想象一下,如果一個索引和一個表一樣長,那麼它将再次成為一個需要檢查的開銷。這就好比字典的目錄非常詳細,但是其長度已經和所有的文字一樣長,這個時候目錄本身的效率就大大下降了。

索引有弊端嗎?

肯定是有的,索引可以提高查詢讀取性能,而它将降低寫入性能。當有索引時,如果更改一條記錄,或者在資料庫中插入一條新的記錄,它将執行兩個寫入操作(一個操作是寫入記錄本身,另一個操作是将更新索引)。是以,在定義索引時,必須牢記以下幾點:

索引表中的每個字段将降低寫入性能。

建議使用表中的唯一值為字段編制索引。

在關系資料庫中充當外鍵的字段必須建立索引,因為它們有助于跨多個表進行複雜查詢。

索引還使用磁盤空間,是以在選擇要索引的字段時要小心。

什麼是聚集索引

聚集索引clustered index也叫聚簇索引,它的定義是:聚集索引的表中資料行的實體順序與列值(一般是主鍵的那一列)的邏輯順序相同,一個表中隻能擁有一個聚集索引。

例如:

為什麼索引可以讓查詢變快?終于有人說清楚了!

結合上面的表格就很好了解了:資料行的實體順序與列值的順序相同,如果我們查詢id比較靠後的資料,那麼這行資料的位址在磁盤中的實體位址也會比較靠後。聚集索引存儲記錄是實體上連續存在,而非聚集索引是邏輯上的連續,實體存儲并不連續。

為什麼查詢更快呢?我們通過上面的分析知道了索引是通過二叉樹的資料結構來描述的,我們可以這麼了解聚簇索引:索引的葉節點就是資料節點。而非聚簇索引的葉節點仍然是索引節點,隻不過有一個指針指向對應的資料塊。

主鍵一般會預設建立聚集索引。

在建立聚集索引之前,應先了解您的資料是如何被通路的。可考慮将聚集索引用于:

包含大量非重複值的列。使用下列運算符傳回一個範圍值的查詢:BETWEEN、>、>=、< 和 <=。被連續通路的列。傳回大型結果集的查詢。經常被使用聯接或 GROUP BY 子句的查詢通路的列;一般來說,這些是外鍵列。對 ORDER BY 或 GROUP BY 子句中指定的列進行索引,可以使 SQL Server 不必對資料進行排序,因為這些行已經排序。這樣可以提高查詢性能。OLTP型的應用程式,這些程式要求進行非常快速的單行查找(一般通過主鍵)。應在主鍵上建立聚集索引。聚集索引不适用于:

頻繁更改的列 這将導緻整行移動,因為 SQL Server 必須按實體順序保留行中的資料值。這一點要特别注意,因為在大資料量事務處理系統中資料是易失的

索引失效的典型例子

條件中用or,即使其中有條件帶索引,也不會使用索引查詢,這就是查詢盡量不要用or的原因,用in吧。

常見的sql優化手段有哪些

1.避免全表掃描

全表掃描往往發生在下面幾種情況:

SQL的on子句或者where子句涉及到的列上沒有索引;

表資料量很小,走索引查詢比全表掃描更麻煩;這對于少于10行且行長度較短的表來說很常見

2.避免索引失效

不在索引列上做任何操作(計算,函數、自動or手動類型轉換),這樣會導緻索引失效而轉向全表掃描。

存儲引擎不能使用索引中範圍條件右邊的列。這個是因為age中查詢時範圍查詢了,pos列的索引就沒有生效了

盡量使用覆寫索引(隻通路索引的查詢(索引列和查詢列一緻)),減少select *。

對于MySQL而言

mysql在使用不等于(!=或者<>)的時候無法使用索引會導緻全表掃描

is null,is not null也無法使用索引

like 通配符開頭'%abc..',mysql索引會失效會變成全表掃描的操作

3.避免排序,不能避免,盡量選擇索引排序

4.避免查詢不必要的字段

5.避免臨時表的建立,删除

原文連結:

https://blog.csdn.net/topdeveloperr/article/details/88742503