數據庫索引的知識點,你所須要瞭解的都在這兒了

數據庫索引,相信你們都不陌生吧。面試

索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息。做爲輔助查詢的工具,合理的設計索引能很大程度上減輕db的查詢壓力,db咱們都知道,是項目最核心也是最薄弱的地方,若是壓力太大很容易產生故障,形成難以預計的影響。因此,無論是平常開發仍是面試,索引這一塊知識體系都是必須掌握的。算法

固然,雖然說是必須掌握,但索引的知識點不少,不少初學者常常會遺漏,這也是我爲何想寫這篇知識點總結的緣由,既是給讀者的分享,也是給本身一次全面的複習,但願對大家有所幫助。數據庫

好了,廢話很少說,進入正題。數組

首先聲明一下,本文索引的知識點所有是基於MySQL數據庫數據結構

索引的優缺點

優勢:函數

1.大大加快數據的查詢速度工具

2.惟一索引能夠保證數據庫表每一行的惟一性性能

3.加速錶鏈接時間搜索引擎

缺點:spa

1.建立、維護索引要耗費時間,因此,索引數量不能過多。

2.索引是一種數據結構,會佔據磁盤空間。

3.對錶進行更新操做時,索引也要動態維護,下降了維護速度

索引的類型

索引的出現是爲了提升查詢效率,可是實現索引的方式卻有不少種,因此這裏也就引入了索引模型的概念。這裏介紹三種經常使用於索引的數據結構,分別是哈希表、有序數組和搜索樹。

哈希索引

哈希表,也稱散列表,主要設計思想是經過一個哈希函數, 把關鍵碼映射的位置去尋找存放值的地方 ,讀取的時候也是直接經過關鍵碼來找到位置並存進去,這種數據結構的平均查找複雜度爲O(1)。

好比咱們維護一張身份證信息和用戶姓名的表,須要根據身份證號查詢姓名,哈希索引大概是這樣的:

這種索引結構優勢在於隨機添加或刪除單個元素的效率高,缺點在於哈希表中的元素並不必定按順序排列,因此若是想作區間查詢的話是很慢的,

假設我想查找圖中身份證號在[ID_card_n1, ID_card_n3]這個區間的全部用戶的話,就必須所有掃描一遍了。

因此,哈希表這種結構適用於只有等值查詢的場景

有序數組索引

有序數組索引在等值查詢和區間查詢場景中的效率都很高,仍是拿上面的圖作例子,用有序數組實現的話是這樣子的:

數組的元素按身份證號有序排列,要查詢數據的時候,使用二分法就能夠快速獲得,時間複雜度爲O(logN),並且,由於是有序排列,查詢某個區間內的數據也是很是的快。

固然,有序數組的缺點也很明顯,就跟ArrayList同樣,雖然搜索快,但添加刪除元素都有可能要移動後面全部的元素,這是數組的自然缺陷。因此,有序數組索引只適用於靜態存儲引擎,好比你要保存的是2017年某個城市的全部人口信息,這類不會再修改的數據。

搜索樹索引

說到搜索樹,咱們最熟悉的應該就是二叉搜索樹了,二叉搜索樹的特色是每一個結點的左兒子小於父結點,父結點又小於右兒子,而且左右子樹也分別爲二叉搜索樹,平均時間複雜度是O(log2(n))。

它既有鏈表的快速插入與刪除操做的特色,又有數組快速查找的優點,同時,由於自己二叉搜索樹是有序的,因此也支持範圍查找

這麼提及來,其實二叉搜索樹來作索引好像也是個不錯的選擇,其實否則

首先咱們要明確的一點是,這棵樹是存在於磁盤中,每次咱們都要從磁盤中讀取出相應的結點,然而二叉搜索樹的結點在文件中是隨機存放的,因此可能讀取一個結點就須要一個磁盤IO,偏偏二叉搜索樹都會比較高,如一棵一百萬個元素的平衡二叉樹就有十幾層高度了,也就是大部分狀況下檢索一次數據就須要十幾回磁盤IO,這個代價過高了,因此通常二叉搜索樹也不會被用來做索引。

爲了讓一個查詢儘可能少地讀磁盤,就必須讓查詢過程訪問儘可能少的數據塊,也就是說,儘量的讓樹的高度變低,也就是用多路搜索樹,而InnoDB存儲引擎使用的就是這種多路搜索樹,也就是咱們常說的B+樹。

InnoDB的索引結構

InnoDB是MySQL中最經常使用的搜索引擎,它的索引底層結構用的就是B+樹,全部的數據都是存儲在B+樹中的。每個索引在InnoDB中對應一顆B+樹。

B+樹的特色是:

  • 全部的葉子結點中包含了所有元素的信息,及指向含這些元素記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。
  • 全部的中間結點元素都同時存在於子結點,在子結點元素中是最大(或最小)元素。

這種結構有兩個優勢:

  • 可使得單一結點存儲更多的元素,除了葉子結點,其餘的結點只是包含了鍵,沒有保存值,這樣的話,樹的高度就能有效下降,從而減小查詢的IO次數;
  • 同時,由於葉子結點包含了下個葉子結點的指針,因此範圍查詢的時候若是搜索到第一個葉子結點的話,就能根據指針指向查詢後面的數據,不用再從根結點遍歷了。這也是爲何不少大神建議表的主鍵設計成自增加的好,由於這樣範圍查詢能提升效率

索引的分類

按照結構來分的話,數據庫索引能夠分爲聚簇索引和非聚簇索引。

聚簇索引,也叫彙集索引,就是按照每張表的主鍵構造一顆B+樹,同時葉子結點中存放的就是整張表的行記錄數據,簡單點說,就是咱們常說的主鍵索引。在聚簇索引之上建立的索引稱之爲輔助索引,輔助索引訪問數據老是須要二次查找。

非聚簇索引,也叫非彙集索引,二級索引。這種索引是將數據與索引分開存儲,索引結構的葉子結點指向了數據對應的位置。

聚簇索引

InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行數據就儲存在葉子節點上,咱們先假設一張用戶表,這張表包含了id,name,company幾個字段,

用圖片表示InnoDB的索引結構大概是這樣:

從圖中就能夠看出,若是咱們使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索算法便可查找到對應的葉結點,以後得到行數據。

若對Name列進行條件搜索,則須要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操做,最終到達葉子節點便可獲取整行數據。(重點在於經過其餘鍵須要創建輔助索引)

這是聚簇索引的結構,而非聚簇索引的表明是MyISM,這也是MySQL中常見的搜索引擎。

非聚簇索引

非聚簇索引的兩棵B+樹看上去沒什麼不一樣,結點的結構徹底一致只是存儲的內容不一樣而已,主鍵索引B+樹的節點存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵。索引自己不存儲數據,數據存儲在獨立的地方,這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據。

看上去,好像非聚簇索引的效率要高於聚簇索引,由於不用查兩次B+樹,那爲何最經常使用的InnoDB引擎還要用這種存儲結構呢?它自己的優點在哪?

一、聚簇索引中,因爲行數據和葉子結點存儲在一塊兒,同一頁中會有多條行數據,訪問同一數據頁不一樣行記錄時,已經把頁加載到了Buffer中,再次訪問的時候,會在內存中完成訪問,沒必要訪問磁盤。這樣主鍵和行數據是一塊兒被載入內存的,找到葉子節點就能夠馬上將行數據返回了,因此,若是按照主鍵Id來組織數據,得到數據更快。

二、輔助索引使用主鍵做爲"指針"而不是使用地址值做爲指針的好處是,減小了當出現行移動或者數據頁分裂時輔助索引的維護工做**,使用主鍵值看成指針會讓輔助索引佔用更多的空間,換來的好處是InnoDB在移動行時無須更新輔助索引中的這個"指針"。**也就是說行的位置(實現中經過16K的Page來定位)會隨着數據庫裏數據的修改而發生變化(前面的B+樹節點分裂以及Page的分裂),使用聚簇索引就能夠保證無論這個主鍵B+樹的節點如何變化,輔助索引樹都不受影響。

三、聚簇索引適合用在排序、範圍查詢,非聚簇索引不適合。

覆蓋索引

說到輔助索引,咱們還能夠延伸出另外一種特別的索引,就是覆蓋索引

上面說了,聚簇索引中訪問數據要通過二次查找,就是先找到輔助鍵的葉子結點,獲得主鍵對應的結點後再用主鍵索引查詢數據,這樣仍是比較慢的,其實,若是咱們所需的字段第一次查找就能獲取到的話,就不用再二次查找主鍵了,也就是不用「回表」。

就仍是上面那張表有三個字段id,name,company的表來講,我給name加了索引,在查詢數據的時候,我就這麼寫語句:

select name from user where name like '張%';

由於咱們的語句走了索引,而且返回的字段在葉子結點都存在,查詢的時候就不會回表了,多好啊~~

因此,若是所需的字段恰好是索引列的話,儘可能用這種查詢方式,不要用select *這種語句。

索引種類

前面說的索引分類是按照結構來分,若是按做用範圍來分的話,索引還能夠分爲如下幾種:

普通索引:這是最基本的索引類型,沒惟一性之類的限制。

CREATE INDEX INDEX_NAME ON TABLE_NAME(PROPERTY_NAME)

惟一性索引:和普通索引基本相同,但全部的索引列只能出現一次,保持惟一性。

CREATE UNIQUE INDEX INDEX_NAME ON TABLE_NAME(PROPERTY_NAME)

主鍵:跟惟一索引同樣,不能有重複的列,但本質上,主鍵不能算是索引,而是一種約束,必須指定爲"PRIMARY KEY"。它跟惟一索引的區別在於:

  • 主鍵建立後必定包含一個惟一性索引,惟一性索引並不必定就是主鍵。
  • 惟一性索引列容許空值,而主鍵列不容許爲空值。
  • 主鍵列在建立時,已經默認爲空值 + 惟一索引了。
  • 主鍵能夠被其餘表引用爲外鍵,而惟一索引不能。
  • 一個表最多隻能建立一個主鍵,但能夠建立多個惟一索引。
  • 主鍵更適合那些不容易更改的惟一標識,如自動遞增列、身份證號等。

全文索引:全文索引的索引類型爲FULLTEXT,能夠在VARCHAR或者TEXT類型的列上建立。在MySQL5.6之前的版本,只有 MyISAM 存儲引擎支持全文索引,5.6及以後的版本,MyISAM 和 InnoDB 存儲引擎均支持全文索引。

CREATE FULLTEXT INDEX INDEX_NAME ON TABLE_NAME(PROPERTY_NAME)

聯合索引:聯合索引其實不是一種索引分類,就是包含多個字段的普通索引,好比有個聯合索引爲index(a,b),查找的時候能夠用 a and b 做爲條件,

最左匹配原則

聯合索引中,最左優先,以最左邊的爲起點任何連續的索引都能匹配上。同時遇到範圍查詢(>、<、between、like)就會中止匹配。

就像上面說的index(a,b)或者是a單獨做爲查詢條件都會走索引,可是若是是單獨用 b 作查詢條件就不會走索引了

或者是若是創建(a,b,c,d)順序的索引的話,用a = 1 and b = 2 and c > 3 and d = 4這樣的語句搜索,d是用不到索引的,由於c字段是一個範圍查詢,它以後的字段會中止匹配。

索引何時會失效

一、索引列用函數或表達式,好比這種

select * from test where  num  +  1 = 5

MySQL沒法解析這種方程,這徹底是用戶的行爲,應該把索引列當成獨立的列,這樣索引纔會生效。

二、存在NULL值條件

select * from user where user_id is not null;

咱們在設計數據庫表時,應該盡力避免NULL值出現,若是數據有爲空的狀況能夠給一個默認值,好比數值型的能夠給0、-1,字符類型的能夠給空字符串。

三、用or表達式做爲條件,有一個列沒有索引,那麼其它列的索引將不起做用

select * from user where user_id = 700 or user_name = "老薛";

像這種,若是user_id有加索引,而user_name沒有的話,那麼執行的時候user_id的索引也是失效的,這也是爲何開發中儘可能少用or的緣由,除非是兩個字段都加了索引。

四、列與列對比,某個表中,有兩列(id和c_id)都建了單獨索引,下面這種查詢條件不會走索引

select * from test where id = c_id;

五、數據類型的轉換。若是列類型是字符串,那必定要在條件中將數據使用引號引用起來,不然不使用索引

create index `idx_user_name` ON user(user_name) select * from user where user_name = 123;

像上面這種,雖然給user_name創建了索引,但查詢的時候條件沒有當成字符串,這樣的話就不會走索引。

六、NOT條件

當查詢條件爲非時,索引定位就困難了,執行計劃此時可能更傾向於全表掃描,這類的查詢條件有:<>、NOT、in、not exists

select * from user where user_id<>500; select * from user where user_id in (1,2,3,4,5); select * from user where user_id not in (6,7,8,9,0); select * from user where user_id exists (select 1 from user_record where user_record.user_id = user.user_id);

七、like查詢是以%開頭

當使用模糊搜索時,儘可能採用後置的通配符,例如要查姓張的人,能夠用user_name like ‘張%’,這樣走索引時,能夠從前面開始匹配索引列,但若是是這樣user_name like ‘%張’,那麼就會走全表掃描的方式

八、多列索引,遵循最左匹配原則,這個上面說了

何時該用索引

前面說了,索引雖然能加快查詢速度,但自己也會佔用空間,因此,索引的建立並非越多越好,爲了使索引能有效應用,咱們要把索引留給最有用的查詢字段,通常來講,應該在這些字段上建立索引:

  • 主鍵字段,這不用多說了吧;
  • 常常須要搜索的列,好比where條件常常用到的字段;
  • 其餘表的外鍵字段,做爲鏈接表的條件字段,能夠有效加快連表查詢速度;
  • 查詢中做爲排序、統計或者是分組的字段;

一樣,對於有些字段不該該建立索引,這些列包括

  • 頻繁更新的字段不適合建立索引,由於每次更新不僅僅是更新記錄,還會更新索引,保存索引文件
  • where條件裏用不到的字段,不建立索引;
  • 表記錄太少,不須要建立索引;
  • 對於那些定義爲text,image類型的列不該該增長索引。這是由於,這些列的數據量要麼至關大,要麼取值不多,不利於使用索引;
  • 數據重複且分佈平均的字段,所以爲常常查詢的和常常排序的字段創建索引。注意某些數據包含大量重複數據,這種字段創建索引就沒有太大的效果,例如性別字段,只有男女,不適合創建索引。

explain關鍵字

explain是MySQL的關鍵字,經過該關鍵字咱們能夠查看搜索語句的性能。

這是查詢表的數量,一共有三千多萬行,這麼多的數據,咱們搜索的時候確定要用到索引才行,至於索引是否會生效,咱們也能夠經過該關鍵字來看下

看,搜索的條數瞬間降到了16條,走的索引是 index_user_id,證實咱們的索引是生效的。

關於explain的幾個重要參數,咱們有必要了解一些:

id:查詢的序列號

select_type:查詢的類型,主要是區別普通查詢和聯合查詢、子查詢之類的複雜查詢。

type

type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:

system > const > eq_ref > ref >fulltext > ref_or_null > index_merge > unique_subquery >index_subquery > range > index > ALL

System效率最高,ALL的話已是全表掃描了,通常來講,查詢至少要達到range級別。

key

顯示MySQL實際決定使用的鍵。若是沒有索引被選擇,鍵是NULL。

`key=primary的話,表示使用了主鍵;

key=null表示沒用到索引。
`

possible_keys

指出MySQL能使用哪一個索引在該表中找到行。若是是空的,沒有相關的索引。這時要檢查語句中是否是有什麼狀況致使索引失效。

rows

表示執行計劃中估計掃描的行數,是個估計值。

Extra

  • 若是是Only index,這意味着信息只用索引樹中的信息檢索出的,這比掃描整個表要快。

  • 若是是where used,就是使用上了where限制。

  • 若是是impossible where 表示用不着where,通常就是沒查出來啥。

  • 出現using index就說明咱們的索引是生效的。

總結

好了,索引的知識點就介紹到這了,最後總結一下索引的注意事項吧。

一、索引要根據表數據的使用狀況來建立,不能建立太多,通常一張表不建議超過6個索引字段

二、好刀要用在刀刃上,常常用於查詢,沒多少重複數據,搜索行數不超過表數據量4%的字段用索引的效果比較好

三、建立聯合索引要注意最左匹配原則,切記,最左邊的字段是必傳字段,這點我他媽就吃過大虧

四、查詢語句要用explain執行計劃來查看性能。

參考:

https://www.jianshu.com/p/fa8192853184

MySQL實戰45講

最後

雖然都是基礎知識,但也花了我一天的時間來整理了,洋洋灑灑五千多字,也算是一篇乾貨了,各位看官以爲有所收穫的話,還望能給鄙人來個轉發或點贊之類的,不求四連,能雙連或者是一連我都很滿意了,大家的舉手之勞就是我不斷創做的動力!

做者:鄙人薛某,一個不拘於技術的互聯網人,歡迎關注個人公衆號,每週不按期更新干貨文章,這裏不只有技術,還有吹水~~~

相關文章
相關標籤/搜索