高性能MySQL(3)——创建高性能索引
索引對于良好的性能非常關(guān)鍵。尤其是當(dāng)表中的數(shù)據(jù)量越來越大時,索引對性能的影響愈發(fā)重要。
一、索引的類型
在MySQL中,索引是在存儲引擎層而不是服務(wù)器層實(shí)現(xiàn)的。所以沒用統(tǒng)一的索引標(biāo)準(zhǔn),不同存儲引擎的索引工作方式并不相同。
1.1、B-Tree
B-Tree索引即使用B-Tree數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)。B-Tree通常意味著所有值都是按順序存儲的,并且每個葉子頁到根的距離相同。存儲引擎已不同的方式來使用B-Tree索引,性能也各不相同。
可以使用B-Tree索引的查詢類型——全鍵值、鍵值范圍和鍵前綴查找。其中鍵前綴查找只適用于根據(jù)最左前綴查找。
1.2、哈希索引
哈希索引基于哈希表實(shí)現(xiàn),只有精確匹配索引的所有列的查詢才有效。在MySQL中,只有Memory引擎顯示支持哈希索引,這也是Memory引擎的默認(rèn)索引類型。
對于每一行數(shù)據(jù),存儲引擎都會對所有的索引列計算一個哈希碼,如果多個列的哈希碼相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希條目中。哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數(shù)據(jù)行的指針。
1.3、全文索引
全文索引是一種特殊類型的索引,它查找的是文本中的關(guān)鍵詞,而不是直接比較索引中 的值。全文搜索和其他幾類索引的匹配方式完全不一樣。它有許多需要注意的細(xì)節(jié),如 停用詞、詞干和復(fù)數(shù)、布爾搜索等。全文索引更類似于搜索引擎做的事情,而不是簡單 的WHERE條件匹配。
在相同的列上同時創(chuàng)建全文索引和基于值的B-Tree索引不會有沖突,全文索引適用于 MATCH AGAINST操作,而不是普通的WHERE條件操作。
1.4、其他索引類別
還有很多第三方的存儲引擎使用不同類型的數(shù)據(jù)結(jié)構(gòu)來存儲索引。例如TokuDB使用分 形樹索引(fractal tree index),這是一類較新開發(fā)的數(shù)據(jù)結(jié)構(gòu),既有B-Tree的很多優(yōu)點(diǎn), 也避免了 B.Tree的一些缺點(diǎn)。如果通讀完本章,可以看到很多關(guān)于InnoDB的主題,包 括聚簇索引、覆蓋索引等。多數(shù)情況下,針對InnoDB的討論也都適用于TokuDB。
二、索引的優(yōu)點(diǎn)
索引可以讓服務(wù)器快速地定位到表的指定位置。但是這并不是索引的唯一作用,到目前 為止可以看到,根據(jù)創(chuàng)建索引的數(shù)據(jù)結(jié)構(gòu)不同,索引也有一些其他的附加作用。總結(jié)下來索引的三大優(yōu)點(diǎn):
- 索引大大減少了服務(wù)器需要掃描的數(shù)據(jù)量;
- 索引可以幫助服務(wù)器避免排序和臨時表;
- 索引可以將隨機(jī)I/O變?yōu)轫樞騃/O。
索引是最好的解決方案嗎?
索引并不總是最好的工具。總的來說,只有當(dāng)索引幫助存儲引擎快速查找到記錄帶 來的好處大于其帶來的額外工作時,索引才是有效的。對于非常小的表,大部分情 況下簡單的全表掃描更高效。對于中到大型的表,索引就非常有效。但對于特大型的表,建立和使用索引的代價將隨之增長。這種情況下,則需要一種技術(shù)可以直接區(qū)分出查詢需要的一組數(shù)據(jù),而不是一條記錄一條記錄地匹配。
三、高性能的索引策略
3.1、獨(dú)立的列
索引列不能是表達(dá)式的一部分,也不能是函數(shù)的參數(shù)。
例如:SELECT actor_id FROM actor WHERE actor_id + 1 = 5;
或者:SELECT actor_id FROM actor WHERE f(actor_id) = 5;
3.2、前綴索引和索引選擇性
有時候需要索引很長的字符列,這會讓索引變得很大且很慢。此時可以有兩個策略,一個是自定義哈希索引,另一個就是前綴索引。
- 前綴索引能大大節(jié)約索引空間,從而提高索引效率,但這樣也會降低索引的選擇性(索引選擇性——不重復(fù)的索引值和數(shù)據(jù)表記錄總數(shù)的比值);
- 索引前綴長度的選擇——計算法。例如:LELECT COUNT(DISTINCT city)/COUNT() AS sel1, COUNT(DISTINCT LEFT(city, 3))/COUNT() AS sel2, …; 如果前綴的選擇性接近sel1就可以使用了。有時候只看平均選擇型也不靠譜,還需要做進(jìn)一步判斷。
- 缺點(diǎn):MySQL無法使用前綴索引做ORDER BY和GROUP BY,也無法使用前綴索引做覆蓋掃描;
- 有時候也可以使用前綴索引——可將對應(yīng)列的字符串反序存儲,并創(chuàng)建前綴索引。
3.3、多列索引
為多列創(chuàng)建合適的索引
- 多列索引。例如:key(col1, col2, col3);
- MySQL5.0之后的版本引入了“索引合并”的策略,一定程度上可以使用表上的多個單列索引來定位表中的行;
- 索引合并策略有時候是一種優(yōu)化后的結(jié)果,但實(shí)際上更說明表上的索引建得很糟糕。
- 當(dāng)出現(xiàn)服務(wù)器對多個索引做相交操作時(多個AND),通常意味著需要一個包含相關(guān)列的多列索引,而不是多個獨(dú)立的單列索引;
- 當(dāng)服務(wù)器需要對多個索引做聯(lián)合操作時(多個OR),通常需要耗費(fèi)大量的CPU和內(nèi)存在算法的緩存、排序和合并上。
3.4、選擇合適的索引順序
- 正確的索引順序依賴于使用該索引的查詢,并且同時需要考慮如何更好的滿足排序和分組的需要;
- 索引可以按照升序或者降序進(jìn)行掃描,以滿足精確符合列順序的ORDER BY 、GROUP BY和DISTINCT等子句的查詢需求;
- 索引列順序的選擇——在不考慮分組和排序的情況下,將選擇性最高的列放到索引最前面(經(jīng)驗(yàn)法則);
- 避免隨機(jī)I/O和排序;
- 對于某些特殊用戶和分組,避免其使用普通的索引查詢。
3.5、聚簇索引
聚簇索引就是按照每張表的主鍵構(gòu)造一顆B+樹,同時葉子節(jié)點(diǎn)中存放的就是整張表的行記錄數(shù)據(jù),也將聚集索引的葉子節(jié)點(diǎn)稱為數(shù)據(jù)頁。這個特性決定了索引組織表中數(shù)據(jù)也是索引的一部分,每張表只能擁有一個聚簇索引。
Innodb通過主鍵聚集數(shù)據(jù),如果沒有定義主鍵,innodb會選擇非空的唯一索引代替。如果沒有這樣的索引,innodb會隱式的定義一個主鍵來作為聚簇索引。
聚簇索引的優(yōu)點(diǎn):
- 數(shù)據(jù)訪問更快,因?yàn)榫鄞厮饕龑⑺饕蛿?shù)據(jù)保存在同一個B+樹中,因此從聚簇索引中獲取數(shù)據(jù)比非聚簇索引更快
- 聚簇索引對于主鍵的排序查找和范圍查找速度非常快
聚簇索引的缺點(diǎn):
- 插入速度嚴(yán)重依賴于插入順序,按照主鍵的順序插入是最快的方式,否則將會出現(xiàn)頁分裂,嚴(yán)重影響性能。因此,對于InnoDB表,我們一般都會定義一個自增的ID列為主鍵
- 更新主鍵的代價很高,因?yàn)閷?dǎo)致被更新的行移動。因此,對于InnoDB表,我們一般定義主鍵為不可更新。
- 二級索引訪問需要兩次索引查找,第一次找到主鍵值,第二次根據(jù)主鍵值找到行數(shù)據(jù)。
3.6、覆蓋索引
通常開發(fā)人員會根據(jù)查詢的where條件來創(chuàng)建合適的索引,但是優(yōu)秀的索引設(shè)計應(yīng)該考慮到整個查詢。其實(shí)mysql可以使用索引來直接獲取列的數(shù)據(jù)。如果索引的葉子節(jié)點(diǎn)包含了要查詢的數(shù)據(jù),那么就不用回表查詢了,也就是說這種索引包含(亦稱覆蓋)所有需要查詢的字段的值,我們稱這種索引為覆蓋索引
3.7、使用索引掃描排序
MySQL有兩種方式可以生成有序結(jié)果:通過排序操作;按照索引順序掃描。
- 只有當(dāng)索引的列順序和ORDER BY子句的順序完全一致,并且所有列的排序方向(升序/降序)都一樣時,MySQL才能使用索引來對結(jié)果做排序;
- 當(dāng)查詢需要關(guān)聯(lián)多張表時,只有當(dāng)ORDER BY子句引用的字段全部來自第一張表時,才能使用索引排序;
- ORDER BY子句中的字段需要滿足索引的最左前綴的要求,才能使用索引排序;
- 當(dāng)索引的前導(dǎo)列為常量時,ORDER BY子句可以不滿足索引的最左前綴要求也能使用索引排序。例如:key(rental_date, inventory_id, customer_id);… where rental_data=‘2018-01-08’ ORDER BY inventory_id DESC;
四、維護(hù)索引和表
維護(hù)表有三個目的:找到并修復(fù)損壞的表;維護(hù)準(zhǔn)確的索引統(tǒng)計信息;減少碎片
4.1、更新索引統(tǒng)計信息
MySQL的查詢優(yōu)化器會通過兩個API來了解存儲引擎的索引值的分布信息,已決定如何使用索引信息。
- records_in_range();
- info()。如果存儲引擎向優(yōu)化器提供的索引統(tǒng)計信息不準(zhǔn)確,就會導(dǎo)致優(yōu)化器做出錯誤的優(yōu)化決定,這會嚴(yán)重影響查詢性能。可通過執(zhí)行ANALYZE TABLE 來重新生成統(tǒng)計信息以解決這個問題。
4.2、減少索引和數(shù)據(jù)的碎片
- B-Tree索引可能會碎片化,碎片化的索引可能會以很差或無序的方式存儲在磁盤上,這會降低查詢效率;
- 表數(shù)據(jù)存儲也可能碎片化。主要有行碎片、行間碎片、剩余空間碎片三種。對于MyISAM表,這三類碎片都可能發(fā)生,但I(xiàn)nnoDB不會出現(xiàn)短小的行碎片,InnoDB會移動短小的行,并重寫到一個片段中。
- 【維護(hù)方法】可通過執(zhí)行POTIMIZE TABLE或者導(dǎo)出再導(dǎo)入來重新整理數(shù)據(jù);對于那些不支持POTIMIZE TABLE命令的引擎,可以執(zhí)行ALTER TABLE操作來重建表。只需要將表的存儲引擎改為當(dāng)前的引擎即可。例如:ALTER TABLE <table> ENGINE=<engine>;
五、總結(jié)
索引是一個非常復(fù)雜的話題! MySQL和存儲引擎訪問數(shù)據(jù)的方式, 加上索引的特性,使得索引成為一個影響數(shù)據(jù)訪問的有力而靈活的工作(無論數(shù)據(jù)是在 磁盤中還是在內(nèi)存中)。
在MySQL中,大多數(shù)情況下都會使用B-Tree索引。其他類型的索引大多只適用于特殊 的目的。如果在合適的場景中使用索引,將大大提高査詢的響應(yīng)時間。
如果一個査詢無法從所有可能的索引中獲益,則應(yīng)該看看是否可以創(chuàng)建一個更合適的索 引來提升性能。如果不行,也可以看看是否可以重寫該査詢,將其轉(zhuǎn)化成一個能夠高效 利用現(xiàn)有索引或者新創(chuàng)建索引的査詢。這也是下一章要介紹的內(nèi)容。
參考:
《高性能 MySQL 第三版》
聚簇索引和非聚簇索引
mysql-覆蓋索引
創(chuàng)建高性能的索引
總結(jié)
以上是生活随笔為你收集整理的高性能MySQL(3)——创建高性能索引的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows下最好的C++ IDE
- 下一篇: 在命令行模式下管理SELinux