問題一:關于數(shù)據(jù)庫索引

什么是索引?索引的種類?

索引是幫助數(shù)據(jù)庫高效獲取數(shù)據(jù)的數(shù)據(jù)結構,是基于數(shù)據(jù)庫表創(chuàng)建的,包含一個表中某些列的值以及記錄對應的地址,并且把這些值存儲在一個數(shù)據(jù)結構中。最常見的就是使用哈希表、B+樹作為索引,項目中我們使用InnoDB引擎,默認的是B+樹。

什么情況下創(chuàng)建索引?

一般來說,在WHERE和JOIN中出現(xiàn)的列需要建立素引,因為MySQL只對<,<=,>,>=,BETWEEN, IN,以及某些時候的LIKE才會使用索引(以通配符%和_開頭作查詢時,MySQL不會使用索引)通常會根據(jù)慢查詢?nèi)罩緛韮?yōu)化 SQL以及判斷是否建索引。

查詢更快、占用空間更小

  1. 適合索引的列是出現(xiàn)在where子句中的列,或者連接子句中指定的列
  2. 基數(shù)較小的表,索引效果較差,沒有必要在此列建立索引
  3. 使用短索引,如果對長字符串列進行索引,應該指定一個前綴長度,這樣能夠節(jié)省大量索引空間,如果搜索詞超過索引前綴長度,則使用索引排除不匹配的行,然后檢查其余行是否可能匹配。
  4. 不要過度索引。索引需要額外的磁盤空間,并降低寫操作的性能。在修改表內(nèi)容的時候,索引會進行更新甚至重構,索引列越多,這個時間就會越長。所以只保持需要的索引有利于查詢即可。
  5. 定義有外鍵的數(shù)據(jù)列一定要建立索引。
  6. 更新頻繁字段不適合創(chuàng)建索引
  7. 若是不能有效區(qū)分數(shù)據(jù)的列不適合做索引列(如性別,男女未知,最多也就三種,區(qū)分度實在太低)
  8. 盡量的擴展索引,不要新建索引。比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可。
  9. 對于那些查詢中很少涉及的列,重復值比較多的列不要建立索引。
  10. 對于定義為text、image和bit的數(shù)據(jù)類型的列不要建立索引。

在創(chuàng)建索引時,怎樣考慮多個字段之間的順序的?

在創(chuàng)建多列索引時,我們根據(jù)業(yè)務需求,where子句中使用最頻繁的一列
放在最左邊,因為MySQ索引查詢會遵循最左前綴匹配的原則,即最左
優(yōu)先,在檢索數(shù)據(jù)時從聯(lián)合素引的最左邊開始匹配。所以當我們創(chuàng)建:
個聯(lián)合索引的時候,如(key1,key2,key3),相當于創(chuàng)建了 (key1)
(key1,key2)和(key1,key2,key3)三個索引,這就是最左匹配原則。

關心過業(yè)務系統(tǒng)里面的sql耗時嗎?統(tǒng)計過慢查詢嗎?對慢查詢都怎么優(yōu)化過?

在業(yè)務系統(tǒng)中,除了使用主鍵進行的查詢,其他的都會在測試庫上測試其耗時,慢查詢的統(tǒng)計主要由運維在做,會定期將業(yè)務中的慢查詢反饋給我們。
慢查詢的優(yōu)化首先要搞明白慢的原因是什么?是查詢條件沒有命中索引?是load了不需要的數(shù)據(jù)列?還是數(shù)據(jù)量太大?慢查詢參考

所以優(yōu)化也是針對這三個方向來的:

  • 首先分析語句,看看是否load了額外的數(shù)據(jù),可能是查詢了多余的行并且拋棄掉了,可能是加載了許多結果中并不需要的列,對語句進行分析以及重寫。
  • 分析語句的執(zhí)行計劃,然后獲得其使用索引的情況,之后修改語句或者修改索引,使得語句可以盡可能的命中索引。
  • 如果對語句的優(yōu)化已經(jīng)無法進行,可以考慮表中的數(shù)據(jù)量是否太大,如果是的話可以進行橫向或者縱向的分表。

為什么使用B+樹?

由于索引是存在于磁盤中,當索引非常大的時候,比如達到幾個G的時候,無法一次加載到內(nèi)存中,所以數(shù)據(jù)庫中索引使用的是查找效率更高的樹形結構。B+樹是平衡多路查找樹,是為磁盤等外存儲設備設計的一種平衡查找樹。

系統(tǒng)從磁盤讀取數(shù)據(jù)到內(nèi)存時是以磁盤塊(block)為基本單位的,位于同一個磁盤塊中的數(shù)據(jù)會被一次性讀取出來,InnoDB存儲引擎中有頁 (Page) 的概念,頁是其磁盤管理的最小單位。InnoDB存儲引擎中默認每個頁的大小為16KB,可通過參數(shù)innodb_page_size設置頁的大小,InnoDB在把磁盤數(shù)據(jù)讀入到磁盤時會以頁為基本單位,在查詢數(shù)據(jù)時如果一個頁中的每條數(shù)據(jù)都能有助于定位數(shù)據(jù)記錄的位置這將會減少磁盤IO次數(shù),提高查詢效率。

B+樹使用有序數(shù)組鏈表+平衡多叉樹改良了B樹的有序數(shù)組+平衡多叉樹;B+樹的關鍵字全部存放在葉子節(jié)點中,非葉子節(jié)點用來做索引,而葉子節(jié)點中有一指針指向一下個葉子節(jié)點。做這個優(yōu)化的目的是為了提高區(qū)間訪問的性能。

舉個例子?

數(shù)據(jù)庫索引采用B+樹的主要原因是B樹在提高了磁盤IO性能的同時并沒有解決元素遍歷效率低下的問題。正是為了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節(jié)點就可以實現(xiàn)整棵樹的遍歷。而且在數(shù)據(jù)庫中基于范圍的查詢是非常頻繁的,也是B+樹的優(yōu)勢所在。

例如:要查 5-10之間的,B+樹一把到5這個標記,再一把到10,然后串起來就行了。而B樹在找到第一個符合條件的數(shù)字5后,訪問完第一個關鍵字所在的塊后,得遍歷這個B樹,獲取下一個塊,直到遇到一個不符合條件的關鍵字。遍歷的過程是比較復雜的。

什么是B+樹?

B+樹的演變要從二叉樹開始,可參考B+樹的演變。前面有提到,系統(tǒng)從磁盤讀取數(shù)據(jù)到內(nèi)存的時候是以磁盤塊為基本單位,將磁盤塊中的數(shù)據(jù)一次性的讀取出來的。如果是二叉樹,如圖(二叉樹1)每一個節(jié)點只存儲一個鍵值對,如果是海量的數(shù)據(jù)的話,那么就會有海量的節(jié)點,那么如果要檢索出需要的數(shù)據(jù),可能就要進行多次的IO,是會導致效率低下的。
這時候就引入了B樹(BalanceTree),每一個節(jié)點成為頁(page),mysql的數(shù)據(jù)讀取單位也就是頁。相對于平衡二叉樹來說,每一個節(jié)點存儲了更多的鍵值和數(shù)據(jù),同時,每一個節(jié)點擁有更多的子節(jié)點,稱為“階”,高度也就相對的比較低,所以,B樹讀取磁盤的IO次數(shù)也會大大的減小,數(shù)據(jù)查找效率也會高。
B+樹的非葉子節(jié)點上是不存儲數(shù)據(jù)的,僅存儲鍵值,因為數(shù)據(jù)庫中頁的大小是固定的(innodb每個頁的大小是16kb,當然可以通過參數(shù)進行配置)不存儲數(shù)據(jù),就可以存儲更多的鍵值,樹就會更矮,更胖一些,B+樹的階數(shù)是等于鍵值的數(shù)量。如果B+樹的一個節(jié)點可以存放1000個鍵值的話,那么就可以存儲大約10億的數(shù)據(jù),根節(jié)點是常駐于內(nèi)存之中的,所以只需要兩次磁盤的IO就可以搞定。因為B+樹的數(shù)據(jù)都是按照順序進行排列的,所以進行范圍查找,排序查找,分組查找以及去重都會很快。而B樹都是在各個節(jié)點上,要困難一些。

二叉樹1
B樹
B+樹

B+樹和hash索引比較起來有什么優(yōu)缺點?

哈希索引適合等值查詢,但是無法進行范圍查詢;哈希索引沒辦法利用索引完成排序以及l(fā)ike ‘xxx%’ 這樣的部分模糊查詢(這種部分模糊查詢,其實本質上也是范圍查詢);哈希索引不支持多列聯(lián)合索引的最左匹配規(guī)則;B+樹索引的關鍵字檢索效率比較平均,哈希索引如果有大量重復鍵值的情況下,哈希索引的效率會很低,可能存在哈希碰撞問題。

B+樹葉子節(jié)點都可以存什么東西?

InnoDB的B+樹可能存儲的是整行數(shù)據(jù),也有可能是主鍵的值,索引B+樹的葉子節(jié)點存儲了整行數(shù)據(jù)的是主鍵索引,也被稱之為聚簇索引。而索引B+樹的葉子節(jié)點存儲了主鍵的值的是非主鍵索引,也被稱之為非聚簇索引。

Innodb和myisam引擎及索引學習參考

聚簇索引和非聚簇索引在查詢時有什么區(qū)別?

主鍵索引查詢只會查一次,而非主鍵索引需要回表查詢多次,通過覆蓋索引也可以只查詢一次,覆蓋索引指一個查詢語句的執(zhí)行只用從索引中就能夠取得,不必從數(shù)據(jù)表中讀取。
MySQL只需要通過索引就可以返回查詢所需要的數(shù)據(jù),這樣避免了查到索引后再返回表操作,減少I/O提高效率。

聚簇索引VS非聚簇索引

聚集索引(聚簇索引):以InnoDB 作為存儲引擎的表,表中的數(shù)據(jù)都會有一個主鍵,即使你不創(chuàng)建主鍵,系統(tǒng)也會幫你創(chuàng)建一個隱式的主鍵。

非聚集索引(非聚簇索引):以主鍵以外的列值作為鍵值構建的 B+ 樹索引,我們稱之為非聚集索引。

緩存知識介紹
一次IO讀寫,可以獲取到16K(需要看操作系統(tǒng)中的配置)大小的資源,讀取到的數(shù)據(jù)區(qū)域為Page(頁)。當需要查詢某個索引的B+樹結構的時候,某些頁被加載到內(nèi)存的緩存區(qū)域中,查詢操作會在內(nèi)存里操作,而不用再次進行IO操作了。當要查詢的行數(shù)據(jù)不在緩存里,才會觸發(fā)新的IO操作。
通過上面的緩存知識來看,如果數(shù)據(jù)存放的位置是相對連續(xù)的,則緩存命中率會很高。而聚簇索引正好就是在磁盤上連續(xù)存放的。因為MyISAM的主索引并非聚簇索引,那么他的數(shù)據(jù)的物理地址(硬盤數(shù)據(jù)區(qū)的編號)相對于聚簇索引是比較凌亂的,拿到這些物理地址,按照合適的算法進行I/O讀取,于是開始不停的尋道,不停的旋轉,且存儲地址跨度過大,也容易導致緩存命中率低。

聚簇索引的優(yōu)缺點

優(yōu)點:
1.數(shù)據(jù)訪問更快,因為聚簇索引將索引和數(shù)據(jù)保存在同一個B+樹中,因此從聚簇索引中獲取數(shù)據(jù)比非聚簇索引更快。聚簇索引對于主鍵的排序查找和范圍查找速度非常快

缺點:
1.插入速度嚴重依賴于插入順序,按照主鍵的順序插入是最快的方式,否則將會出現(xiàn)頁分裂,嚴重影響性能。因此,對于InnoDB表,我們一般都會定義一個自增的ID列為主鍵。
2.更新主鍵的代價很高,因為將會導致被更新的行移動。因此,對于InnoDB表,我們一般定義主鍵為不可更新。
3.二級索引訪問需要兩次索引查找,第一次找到主鍵值,第二次根據(jù)主鍵值找到行數(shù)據(jù)。

以下參考自 鏈接

myisam的主索引和次索引都指向物理行(磁盤位置)。
innodb的主鍵下存儲該行的數(shù)據(jù),此索引指向對主鍵的引用。

myisam的索引存儲圖如下,可以看出,無論是id還是cat_id,下面都存儲有執(zhí)行物理地址的值。通過主鍵索引或者次索引來查詢數(shù)據(jù)的時候,都是先查找到物理位置,然后再到物理位置上去尋找數(shù)據(jù)。

myisam的索引查詢示意圖

innodb的索引存儲圖如下,我們會發(fā)現(xiàn),主鍵索引下面直接存儲有數(shù)據(jù),而次索引下,存儲的是主鍵的id。通過主鍵查找數(shù)據(jù)的時候,就會很快查找到數(shù)據(jù),但是通過次索引查找數(shù)據(jù)的時候,需要先查找到對應的主鍵id,然后才能查找到對應的數(shù)據(jù)。

innodb的索引查詢示意圖
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容