自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇帶給你MySQL高性能索引

數(shù)據(jù)庫 MySQL
在MySQL中,索引是在存儲(chǔ)引擎層實(shí)現(xiàn)的而不是Server層實(shí)現(xiàn)的,所以不同的存儲(chǔ)引擎的索引的工作方式是不一樣的。我們對索引的分析應(yīng)該是建立在存儲(chǔ)引擎的基礎(chǔ)上的,InnoDB是MySQL默認(rèn)的存儲(chǔ)引擎。

什么是索引

索引又可以稱為鍵(key)是存儲(chǔ)引擎用于快速找到記錄的一種數(shù)據(jù)結(jié)構(gòu)。

索引是提高M(jìn)ySQL查詢性能最有效的手段,我們常說的MySQL性能調(diào)優(yōu)基本都是對索引的優(yōu)化。所以這是每個(gè)開發(fā)需要掌握并會(huì)應(yīng)用的知識點(diǎn)。

索引是一種數(shù)據(jù)結(jié)構(gòu),它也是存儲(chǔ)在磁盤的一個(gè)文件。上一篇我們學(xué)習(xí)MySQL的邏輯架構(gòu)的時(shí)候了解了InnoDB和MyISM存儲(chǔ)引擎,InnoDB存儲(chǔ)引擎索引和數(shù)據(jù)是同一個(gè)文件,MyISAM索引和數(shù)據(jù)是兩個(gè)獨(dú)立的文件。

在MySQL中,索引是在存儲(chǔ)引擎層實(shí)現(xiàn)的而不是Server層實(shí)現(xiàn)的,所以不同的存儲(chǔ)引擎的索引的工作方式是不一樣的。我們對索引的分析應(yīng)該是建立在存儲(chǔ)引擎的基礎(chǔ)上的,InnoDB是MySQL默認(rèn)的存儲(chǔ)引擎。

索引的優(yōu)點(diǎn):

  • 索引大大減少了服務(wù)器需要掃描的數(shù)據(jù)量。
  • 索引可以幫助服務(wù)器避免排序和臨時(shí)表。
  • 索引可以隨機(jī)I/O變?yōu)轫樞騃/O。

索引的缺點(diǎn):

  • 索引是數(shù)據(jù)結(jié)構(gòu),它占用了額外的磁盤空間。
  • 當(dāng)表數(shù)據(jù)量比較大時(shí),維護(hù)索引的代價(jià)比較大。

索引數(shù)據(jù)模型

每個(gè)存儲(chǔ)引擎的數(shù)據(jù)結(jié)構(gòu)和算法都是存在區(qū)別,我們先看下MySQL本身支持的索引類型。

B-Tree索引

一般我們說的索引結(jié)構(gòu)就是指B-Tree索引,MySQL大部分的存儲(chǔ)引擎都支持這種索引,但是不同的存儲(chǔ)引擎以不同的方式使用B-Tree索引,性能也各有不同。InnoDB使用的是B+Tree,按照原有的數(shù)據(jù)格式進(jìn)行存儲(chǔ),根據(jù)主鍵引用被索引的行。

B-Tree所有的值都是按順序存儲(chǔ)的,并且每一個(gè)葉子到根的距離相同。下圖是B-Tree的抽象圖:

B-Tree能夠加快訪問數(shù)據(jù)的速度。

存儲(chǔ)引擎不需要全表掃描來獲取所需要的數(shù)據(jù),它是從索引的根節(jié)點(diǎn)開始搜索。根節(jié)點(diǎn)的槽中存放指向子節(jié)點(diǎn)的指針,搜索引擎根據(jù)這些指針向下層查找。通過比較節(jié)點(diǎn)頁的值和要查找的值可以找到合適的指針進(jìn)入到下層節(jié)點(diǎn)。最終引擎要么找到對應(yīng)的值,要么該記錄不存在。

B-Tree的索引如果多個(gè)列,索引值的排序是按照建表時(shí)定義的索引順序,所以索引的順序是比較重要的。

B-Tree是N叉樹,N的大小取決于數(shù)據(jù)塊的大小。

以InnoDB的一個(gè)整數(shù)字段索引為例,N大概為1200,當(dāng)樹高是4的時(shí)候,就可以存1200的3次方的數(shù)據(jù),大概為17億。一個(gè)擁有10億的表上一個(gè)整數(shù)字段的索引,查找一個(gè)值最多訪問3次磁盤。其實(shí)在應(yīng)用時(shí),如果第二層被提前加載到內(nèi)存中,那么磁盤的訪問次數(shù)就更少了。

哈希索引

哈希索引是基于哈希表實(shí)現(xiàn)的,只有精確匹配所有列的查詢才有效。

對于每一行數(shù)據(jù),存儲(chǔ)引擎都會(huì)對所有的索引列計(jì)算一個(gè)哈希碼(hash code),哈希碼是一個(gè)比較小的值,并且不同鍵值的行計(jì)算出來的哈希碼也不一樣。哈希索引將所有的哈希碼存儲(chǔ)在索引中,同時(shí)在hash表中保存指向每個(gè)數(shù)據(jù)行的指針。

創(chuàng)建表test_hash,它的存儲(chǔ)引擎為memory,索引為full_name,索引類型為hash。

  1. CREATE TABLE `test_hash` ( 
  2.   `full_name` varchar(255) DEFAULT NULL
  3.   `short_name` varchar(32) DEFAULT NULL
  4.   `age` int(11) DEFAULT NULL
  5.   KEY `idx` (`full_name`) USING HASH 
  6. ) ENGINE=MEMORY DEFAULT CHARSET=utf8; 

 表中的數(shù)據(jù)如下:

  1. mysql> select * from test_hash; 
  2. +-------------------+------------+------+ 
  3. | full_name         | short_name | age  | 
  4. +-------------------+------------+------+ 
  5. | Dwayne Johnson    | Johnson    | NULL | 
  6. | Taylor Swift      | Taylor     | NULL | 
  7. | Leonardo DiCaprio | Leonardo   | NULL | 
  8. | Vin Diesel        | Diesel     | NULL | 
  9. | Kobe Bryant       | Kobe       | NULL | 
  10. +-------------------+------------+------+ 
  11. rows in set (0.00 sec) 

 那么哈希索引的數(shù)據(jù)結(jié)構(gòu)可能是:

 

 

 當(dāng)我們執(zhí)行查詢語句:

  1. mysql> select short_name from test_hash where full_name = 'Dwayne Johnson'

這個(gè)sql語句的執(zhí)行流程:

1)根據(jù)where條件 'Dwayne Johnson'計(jì)算出哈希碼,那么得到的哈希碼為1234。

2)MySQL在索引中查找到1234,并根據(jù)這個(gè)值找到了對應(yīng)的行記錄指針。

3)根據(jù)指針地址找到對應(yīng)的行,最后比較這個(gè)行中的full_name列是否為'Dwayne Johnson'。

那現(xiàn)在有個(gè)問題,哈希碼沖突的時(shí)候怎么辦呢?學(xué)過HashMap的小伙伴此時(shí)肯定靈機(jī)一動(dòng):哈希碼沖突的時(shí)候使用鏈表。對的,當(dāng)鍵值的哈希碼沖突的時(shí)候,MySQL也是使用的鏈表結(jié)構(gòu)。如果是鏈表結(jié)構(gòu),在查找的時(shí)候就需要遍歷每個(gè)鏈表指針指向的行記錄做匹配,所以哈希沖突比較大的時(shí)候查找的效率是比較低的。

從上面的示例我們可以看出,哈希索引的結(jié)構(gòu)中只存儲(chǔ)了哈希值,它的結(jié)構(gòu)是比較緊湊的,對于精確查詢的效率是比較快的。

但是哈希索引還是有些限制的:

  • 哈希索引中存儲(chǔ)的是鍵值的哈希值,它不是按照索引列的順序的,所以它不無法用于排序。
  • 哈希索引不支持部分索引匹配查找,因?yàn)楣K饕冀K是索引列的全部內(nèi)容。如果我們索引有兩個(gè)列(A,B),查詢的時(shí)候只想使用A列,這個(gè)時(shí)候是無法應(yīng)用索引的。
  • 哈希索引只支持等值查詢,比如=、in等,它不支持任何范圍查詢。
  • 當(dāng)哈希沖突的時(shí)候,存儲(chǔ)引擎必須要遍歷鏈表中的所有行指針,逐行比較,直到找到所有符合條件的行,如果哈希沖突比較多的時(shí)候,索引維護(hù)的代價(jià)比較高。

在MySQL中,目前只有memory引擎顯式支持哈希索引。

InnoDB索引模型

我們前面提到,InnoDB的索引結(jié)構(gòu)是B+Tee,它是以主鍵引用被索引的行。所以在InnoDB中,表都是根據(jù)主鍵順序以索引的形式存放的,每一個(gè)索引在InnoDB里面對應(yīng)一棵B+樹。

B+Tree索引

B+Tree是我們前面提到的B-Tree的擴(kuò)展,B-Tree的每一個(gè)節(jié)點(diǎn)都包含了數(shù)據(jù)項(xiàng),這樣每一塊磁盤存儲(chǔ)的索引值就會(huì)比較少,樹的高度就會(huì)變大,查詢的磁盤I/O次數(shù)就會(huì)增加。

那B+Tree是怎么樣的數(shù)據(jù)結(jié)構(gòu)呢?下圖是B+Tree的抽象圖:

B+Tree與B-Tree的區(qū)別:

  • B+Tree的非葉子節(jié)點(diǎn)不保存數(shù)據(jù)信息,只保存索引值和指向下一層節(jié)點(diǎn)的指針。
  • B+Tree的葉子節(jié)點(diǎn)保存了數(shù)據(jù)
  • B+Tree的葉子節(jié)點(diǎn)是順序排列的,并且葉子相鄰節(jié)點(diǎn)之間有指針的互相引用

B+Tree能夠更好地配合磁盤的讀寫特性,減少單次查詢的磁盤訪問次數(shù)。

InnoDB的索引類型分為主鍵索引和非主鍵索引。

主鍵索引和非主鍵索引

創(chuàng)建表user,它的存儲(chǔ)引擎為InnoDB,id為主鍵,name為普通索引。

  1. CREATE TABLE `user` ( 
  2.   `id` int(10) NOT NULL
  3.   `namevarchar(32) DEFAULT NULL
  4.   `age` int(3) DEFAULT NULL
  5.   `sex` varchar(1) DEFAULT NULL
  6.   `comment` varchar(255) DEFAULT NULL
  7.   `datedate DEFAULT NULL
  8.   PRIMARY KEY (`id`), 
  9.   KEY `idx` (`name`) USING BTREE 
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

 表中的數(shù)據(jù)如下: 

  1. mysql> select * from user
  2. +----+-------+------+------+---------+------------+ 
  3. | id | name  | age  | sex  | comment | date       | 
  4. +----+-------+------+------+---------+------------+ 
  5. |  1 | Alen  |   20 | 1    | NULL    | 2021-02-16 | 
  6. |  2 | Alex  |   21 | 1    | NULL    | 2021-02-16 | 
  7. |  3 | Saria |   16 | 0    | NULL    | 2021-02-16 | 
  8. |  4 | Semyt |   18 | 0    | NULL    | 2021-02-16 | 
  9. |  5 | Summy |   17 | 1    | NULL    | 2021-02-16 | 
  10. |  6 | Tom   |   19 | 0    | NULL    | 2021-02-16 | 
  11. +----+-------+------+------+---------+------------+ 
  12. rows in set (0.00 sec) 

 主鍵索引也稱為聚簇索引,它的葉子節(jié)點(diǎn)都包含了主鍵值、事務(wù)ID、用于事務(wù)和MVCC的回滾指針以及所有剩余的列。

 

  1. mysql> select * from user where id = 1; 

主鍵索引只需要搜索ID這棵B+Tree就可以拿到符合條件的行記錄。

InnoDB是通過主鍵索引聚集數(shù)據(jù),如果表中沒有定義主鍵,InnoDB會(huì)選擇一個(gè)唯一的非空索引代替。如果沒有這樣的索引,InnoDB會(huì)隱式定義一個(gè)主鍵來作為聚簇索引。這也是勾勾為每個(gè)表都創(chuàng)建主鍵的原因。

聚簇索引的優(yōu)點(diǎn):

  • 把相關(guān)的數(shù)據(jù)保存在一起,減少了磁盤I/O;
  • 聚簇索引將數(shù)據(jù)和索引保存在同一顆BTree上,數(shù)據(jù)訪問更快;

聚簇索引的缺點(diǎn):

  • 如果數(shù)據(jù)都在內(nèi)存中,聚簇索引的查詢性能就沒有那么好的優(yōu)勢了。
  • 插入的速度嚴(yán)重依賴于插入順序。盡量保證主鍵索引是有序的。
  • 更新聚簇索引列的代價(jià)更高。
  • 在插入行或者更新主鍵的時(shí)候?qū)е滦枰苿?dòng)行的時(shí)候可能導(dǎo)致頁分裂的問題。當(dāng)插入到一個(gè)已滿的頁中,存儲(chǔ)引擎會(huì)將該頁分裂為兩頁來容納數(shù)據(jù),頁分裂會(huì)導(dǎo)致占用更多的磁盤空間。

非主鍵索引也稱為非聚簇索引,在InnoDB中又被稱為二級索引。非主鍵索引的葉子節(jié)點(diǎn)內(nèi)容是主鍵的值。

  1. mysql> select * from user where name = 'Alen'

非主鍵索引查詢時(shí),首先根據(jù)name普通查詢搜索name索引樹,找到id為1,再根據(jù)id=1到ID索引樹查詢一次才能獲取到符合條件的行記錄。

我們把先搜索普通索引樹得到主鍵,再搜索主鍵索引樹的過程稱為回表。

普通索引的查詢比主鍵索引多檢索了一棵B+Tree,在實(shí)際應(yīng)用場景下如果能用到主鍵索引盡量選擇主鍵索引。

在創(chuàng)建索引的時(shí)候還有其他的原則,我們接下來繼續(xù)學(xué)習(xí)高性能的索引策略。

索引策略

小伙伴們在學(xué)習(xí)索引策略的時(shí)候可以利用上一篇文章的explian關(guān)鍵字查詢執(zhí)行計(jì)劃。

索引的選擇

索引的分類有多種,我們可以按照索引字段的個(gè)數(shù)將索引分為單列索引和聯(lián)合索引。

單列索引:一個(gè)索引只包含一個(gè)列,一個(gè)表中可以多個(gè)單列索引。

聯(lián)合索引:一個(gè)索引包含多個(gè)列。

我們還可以將索引分為普通索引、唯一索引和主鍵索引。

普通索引:基本的索引類型,常用來提高查詢效率,對數(shù)據(jù)沒有限制。允許在索引列中插入空值和重復(fù)值。

唯一索引:索引列中的值必須是唯一的,允許存在空值。

主鍵索引:不允許空值的特殊的唯一索引。

索引有這么多分類,我們在創(chuàng)建索引的時(shí)候如何選擇呢?

索引的三星系統(tǒng):

  • 一星:索引相關(guān)的記錄放到一起。
  • 二星:索引中的數(shù)據(jù)順序和查找列中的順序一致。
  • 三星:索引的列包含了查詢中需要的全部列。

正確的創(chuàng)建和使用索引是實(shí)現(xiàn)高性能查詢的基礎(chǔ)。索引的選擇沒有絕對的要求,主要是根據(jù)自己的業(yè)務(wù)需求,但是有些原則我們在創(chuàng)建索引的時(shí)候可以作為參考。

  • 索引列的區(qū)分度越高則查詢效率越高。
  • 將頻繁搜索的列加入索引,可以提高搜索效率。
  • 索引不只提高了查詢效率,也可以參與排序和分組,經(jīng)常用來排序和分組的字段也需考慮加入索引。
  • 創(chuàng)建索引時(shí),應(yīng)將區(qū)分度高的字段排在前面。即需要注意索引字段的順序。
  • 索引列不能參與任何運(yùn)算。
  • 避免創(chuàng)建重復(fù)索引,即在同一個(gè)列上按照相同的順序創(chuàng)建相同類型的索引。
  • 對于從未使用的索引,應(yīng)盡量刪除。
  • 對于blob、text或者長varchar類型的列,必須要使用前綴索引,取最夠長的前綴來保證較高的區(qū)分度。
  • 普通索引和唯一索引在查詢效率上差別并不大,因?yàn)橐媸前凑枕撟x取數(shù)據(jù)。對于唯一索引在查詢的時(shí)候只要找到就不再繼續(xù)比較了,因?yàn)樗饕呀?jīng)保證了唯一性。而對于普通索引則在找到滿足條件的記錄后還需要繼續(xù)查找直到找到不滿足條件的第一條記錄,但是對于按照頁讀取數(shù)據(jù)的引擎來說,多一次的判斷對性能的影響較小。普通索引和唯一索引的選擇除了保證業(yè)務(wù)的準(zhǔn)確性之外,其他更多的考慮更新數(shù)據(jù)時(shí)對性能的影響。

獨(dú)立的列

”獨(dú)立的列“是指索引不能是表達(dá)式的一部分,也不能是函數(shù)的參數(shù)。

例如,如下sql語句,在查詢時(shí)索引字段name參與了函數(shù)運(yùn)算,會(huì)導(dǎo)致索引失效,全表掃描。

  1. mysql> select * from user where CONCAT(name,'n') = 'Alen'

添加索引age字段,如果我們在查詢的時(shí)候?qū)ge字段進(jìn)行了運(yùn)算也會(huì)導(dǎo)致索引失效:

  1. mysql> select * from user where age + 1 = 21; 

我們平時(shí)開發(fā)中要養(yǎng)成簡化where條件的習(xí)慣,始終使用單獨(dú)的索引列。

覆蓋索引

如果我們把按照普通索引查詢的sql語句修改如下:

  1. mysql> select name from user where name like 'Al%'

這時(shí)只需要查詢普通索引樹即可得到要查詢的列,因?yàn)橐樵兊牧幸呀?jīng)在索引樹了,而不需要再回表查詢。

這種索引字段覆蓋了我們需要查詢的結(jié)果字段的場景我們稱為覆蓋索引。

覆蓋索引可以減少回表,減少索引樹的搜索次數(shù),顯著提高查詢性能,所以覆蓋索引是一個(gè)比較好的優(yōu)化策略。

在實(shí)際開發(fā)中,可以按照業(yè)務(wù)需要把一些常用的檢索字段添加到索引中,利用覆蓋索引提高查詢效率,但是有些場景下不能為了使用覆蓋索引而過多的維護(hù)索引,畢竟索引的維護(hù)成本也是很高的。

最左前綴

這個(gè)時(shí)候我們還需要思考一個(gè)問題,在業(yè)務(wù)場景中我們的查詢是多樣化的,不能為了使用索引而為每一種場景都設(shè)計(jì)一個(gè)索引吧?

這個(gè)時(shí)候我們就要利用B+Tree樹索引結(jié)構(gòu)的另外一個(gè)特性最左前綴。

最左前綴可以是聯(lián)合索引的最左的幾個(gè)字段,也可以是字符串索引的最左的幾個(gè)字符。

創(chuàng)建聯(lián)合索引(name,age),順序一致。

此時(shí)執(zhí)行sql語句:

  1. mysql> select * from user where name = 'Alen'

雖然是聯(lián)合索引,但是name字段排在第一位,也是可以命中索引的。

  1. mysql> select * from user where name like 'Al%'

如果使用name索引字段的最左N個(gè)字符串,也是可以命中索引的。但是如果我們使用%Al是不能命中索引的。

如果我們使用如下的sql查詢語句:

  1. mysql>  select * from user where age = '16'; 

雖然age也是聯(lián)合索引的字段,但是他的順序在name之后,直接使用age查詢無法命中索引。所以創(chuàng)建聯(lián)合索引時(shí)一定要考慮索引字段的順序。

索引維護(hù)時(shí)有一個(gè)原則:如果能通過調(diào)整索引順序,可以少維護(hù)一個(gè)索引,那么就需要優(yōu)先調(diào)整順序而不是增加索引。

MySQL可以利用同一個(gè)索引進(jìn)行排序和掃描行,但是只有當(dāng)索引的列順序和order by子句的順序完全一致,并且列的排序方向都一致(正序或者倒序)時(shí),MySQL才能使用對結(jié)果進(jìn)行排序。

order by子句和查詢類型限制是一樣的,也需要滿足”最左前綴“的原則,否則MySQL無法利用索引排序。

索引下推

當(dāng)我們的查詢語句不滿足最左前綴的時(shí)候會(huì)如何呢?

比如我們查詢名字第一個(gè)字為A,年齡為20,并且性別為1(男)的人員信息,sql語句如下:

  1. mysql>  select * from user where name like 'A%' and age = 20 and sex = 1 ; 

按照我們前面學(xué)習(xí)的最左前綴原則,按照’A‘先搜索到第一個(gè)滿足條件的主鍵1,然后回表查詢判斷其他的兩個(gè)條件是否滿足。

MySQL5.6之后引入了索引下推的優(yōu)化,即會(huì)按照索引中包含的字段優(yōu)先過濾,減少回表的次數(shù)。

我們上述的sql語句在MySQL5.6之前會(huì)回表2次分別對比主鍵1和2兩條的數(shù)據(jù)的其他條件是否滿足,但是引入索引下推的優(yōu)化之后age = 20這個(gè)條件不滿的會(huì)直接過濾掉,只需要對主鍵1回表一次就可以獲取到結(jié)果。

總結(jié)

索引是用于快速查找數(shù)據(jù)的一種數(shù)據(jù)結(jié)構(gòu)。

創(chuàng)建并合理的使用索引是提高查詢效率的基礎(chǔ),在了解索引的數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)之后我們也應(yīng)該花時(shí)間去掌握高性能的索引策略,了解在實(shí)際開發(fā)中創(chuàng)建索引的一些原則,合理的選擇索引。

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2022-03-03 09:05:17

索引MySQL數(shù)據(jù)查詢

2022-03-29 08:18:32

位圖算法索引技術(shù)

2021-04-01 10:51:55

MySQL鎖機(jī)制數(shù)據(jù)庫

2021-03-12 09:21:31

MySQL數(shù)據(jù)庫邏輯架構(gòu)

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2021-07-21 09:48:20

etcd-wal模塊解析數(shù)據(jù)庫

2021-06-21 14:36:46

Vite 前端工程化工具

2021-01-28 08:55:48

Elasticsear數(shù)據(jù)庫數(shù)據(jù)存儲(chǔ)

2021-04-14 14:16:58

HttpHttp協(xié)議網(wǎng)絡(luò)協(xié)議

2022-04-29 14:38:49

class文件結(jié)構(gòu)分析

2023-03-29 07:45:58

VS編輯區(qū)編程工具

2024-06-13 08:34:48

2022-03-22 09:09:17

HookReact前端

2022-02-17 08:53:38

ElasticSea集群部署

2021-04-08 11:00:56

CountDownLaJava進(jìn)階開發(fā)

2021-04-14 07:55:45

Swift 協(xié)議Protocol

2022-02-25 15:50:05

OpenHarmonToggle組件鴻蒙

2021-07-08 07:30:13

Webpack 前端Tree shakin

2023-03-13 09:31:04

2021-10-28 08:51:53

GPIO軟件框架 Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號