MySQL Query 的優化 合理設計並利用索引

2021-07-04 20:59:52 字數 4803 閱讀 1937

索引,可以說是資料庫相關優化尤其是在 query 優化中最常用的優化手段之一了。但是很多人在大部分時候都只是大概了解索引的用途,知道索引能夠讓 query 執行的更快,而並不知道為什麼會更快。尤其是索引的實現原理,儲存方式,以及不同索引之間的區別等就更不是太清楚了。正因為索引對我們的 query 效能影響很大,所以我們更應該深入理解 mysql 中索引的基本實現,以及不同索引之間的區別,才能分析出如何設計出最優的索引來最大幅度的提公升 query 的執行效率。

在 mysql 中,主要有四種型別的索引,分別為:b-tree 索引,hash 索引,fulltext 索引和 r-tree 索引。

b-tree 索引是 mysql 資料庫中使用最為頻繁的索引型別,除了 archive 儲存引擎之外的其他所有的儲存引擎都支援 b-tree 索引。不僅僅在 mysql 中是如此,實際上在其他的很多資料庫管理系統中b-tree 索引也同樣是作為最主要的索引型別,這主要是因為 b-tree 索引的儲存結構在資料庫的資料檢索中有非常優異的表現。

innodb 儲存引擎中,存在兩種不同形式的索引,一種是 cluster 形式的主鍵索引(primarykey),另外一種則是和其他儲存引擎(如 myisam 儲存引擎)存放形式基本相同的普通 b-tree 索引,這種索引在 innodb 儲存引擎中被稱為 secondary index。下面我們通過圖示來針對這兩種索引的存放形式做乙個比較。

圖示中左邊為 clustered 形式存放的 primary key,右側則為普通的 b-tree 索引。兩種索引在root node 和 branch nodes 方面都還是完全一樣的。而 leaf nodes 就出現差異了。在 primary key中,leaf nodes 存放的是表的實際資料,不僅僅包括主鍵欄位的資料,還包括其他欄位的資料,整個資料以主鍵值有序的排列。而 secondary index 則和其他普通的 b-tree 索引沒有太大的差異,只是在leaf nodes 除了存放索引鍵的相關資訊外,還存放了 innodb 的主鍵值。

所以,在 innodb 中如果通過主鍵來訪問資料效率是非常高的,而如果是通過 secondary index 來訪問資料的話,innodb 首先通過相應的索引鍵檢索到 leaf node之後,需要再通過 leaf node 中存放的主鍵值再通過主鍵索引來獲取相應的資料行。

myisam 儲存引擎的主鍵索引和非主鍵索引差別很小,只不過是主鍵索引的索引鍵是乙個唯一且非空的鍵而已。而且 myisam 儲存引擎的索引和 innodb 的 secondary index 的儲存結構也基本相同,主要的區別只是 myisam 儲存引擎在 leaf nodes 上面除了存放索引鍵資訊之外,再存放能直接定位到myisam 資料檔案中相應的資料行的資訊(如 row number),但並不會存放主鍵的鍵值資訊

hash 索引在 mysql 中使用的並不是很多,目前主要是 memory 儲存引擎使用,而且在 memory 儲存引擎中將 hash 索引作為預設的索引型別。這裡不再做介紹。

full-text 索引也就是我們常說的全文索引,目前在 mysql 中僅有 myisam 儲存引擎支援,而且也並不是所有的資料型別都支援全文索引。目前來說,僅有 char varchar 和 text 這三種資料型別的列可以建 full-text 索引。

一般來說,fulltext 索引主要用來替代效率低下的 like 『%*%』 操作,而且能通過多欄位組合的 full-text 索引一次全模糊匹配多個字段。

full-text 索引和普通的 b-tree 索引的實現區別較大,雖然他同樣是以 b-tree 形式來存放索引資料,但是他並不是通過字段內容的完整匹配,而是通過特定的演算法,將字段資料進行分隔後再進行的索引。一般來說 mysql 系統會按照四個位元組來分隔。在整個 full-text 索引中,儲存內容被分為兩部分,一部分是分隔前的索引字串資料集合,另一部分是分隔後的詞(或者片語)的索引資訊。所以,full-text 索引中,真正在 b-tree 索引細細中的並不是我們表中的原始資料,而是分詞之後的索引資料。在 b-tree 索引的節點資訊中,存放了各個分隔後的詞資訊,以及指向包含該詞的分隔前字串資訊在索引資料集合中的位置資訊。

此外,有一點是需要大家注意的,mysql 目前的 full-text 索引在中文支援方面還不太好,需要借助第三方的補丁或者外掛程式來完成。而且 full-text 的建立所消耗的資源也是比較大的,所以在應用於實際生產環境之前還是盡量做好評估。

r-tree 索引可能是我們在其他資料庫中很少見到的一種索引型別,主要用來解決空間資料檢索的問題。這裡不再做介紹。

索引的利處:索引能夠提高資料檢索的效率,降低資料庫的 io 成本;索引還有乙個非常重要的用途,那就是降低資料的排序成本,極大的降低 cpu 資源的消耗。

索引的弊端:修改新增了索引的列資料時,需要同時修改索引資訊。此外,索引是需要占用儲存空間的。

1、如何判定是否需要建立索引

並沒有乙個非常明確的定律可以清晰的定義出什麼字段應該建立索引什麼字段不該建立索引,但我們還是能夠找到幾點基本的判定策略來幫助我們分析是否需要建立索引。

1、較頻繁的作為查詢條件的字段應該建立索引

2、唯一性太差的字段不適合單獨建立索引,即使頻繁作為查詢條件(如狀態字段);

3、更新非常頻繁的字段不適合建立索引

4、不會出現在 where 子句中的字段不該建立索引;

2、單鍵索引還是組合索引

經常會有多個字段一起作為查詢過濾條件存在於 where 子句中。在這種時候,我們就必須要作出判斷,是該僅僅為過濾性最好的字段建立索引還是該在所有字段(過濾條件中的)上面建立乙個組合索引呢?

對於這種問題,很難有乙個絕對的定論,我們需要從多方面來分析考慮,平衡兩種方案各自的優劣,然後選擇一種最佳的方案來解決。組合索引中因為有多個欄位的存在,理論上被更新的可能性肯定比單鍵索引要大很多,這樣可能帶來的附加成本也就比單鍵索引要高。但是,當我們的 where 子句中的查詢條件含有多個欄位的時候,通過這多個字段共同組成的組合索引的查詢效率肯定比僅僅只用過濾條件中的某乙個字段建立的索引要高。

可能有些朋友會說,那我們可以通過建立多個單鍵索引啊。mysql query optimizer 大多數時候都只會選擇其中的乙個索引,然後放棄其他的索引。即使他選擇了同時利用兩個或者更多的索引通過 index_merge 來優化查詢,可能所收到的效果並不會比選擇其中某乙個單鍵索引更高效。因為如果選擇通過 index_merge 來優化查詢,就需要訪問多個索引,同時還要將通過訪問到的幾個索引進行 merge操作,所帶來的成本可能反而會比選擇其中乙個最有效的索引來完成查詢更高。

在一般的應用場景中,只要不是其中某個過濾欄位在大多數場景下都能過濾出90%以上的資料,而且其他的過濾欄位會存在頻繁的更新,我一般更傾向於建立組合索引,尤其是在併發量較高的場景下更是應該如此。因為當我們的併發量較高的時候,即使我們為每個 query 節省很少的 io 消耗,但因為執行量非常大,所節省的資源總量仍然是非常可觀的。

當然,我們建立組合索引並不是說就需要將查詢條件中的所有欄位都放在乙個索引中,我們還應該盡量讓乙個索引被多個 query 語句所利用,儘量減少同乙個表上面索引的數量,減少因為資料更新所帶來的索引更新成本,同時還可以減少因為索引所消耗的儲存空間。

此外,mysql 還為我們提供了乙個減少優化索引自身的功能,那就是字首索引。在 mysql 中,我們可以僅僅使用某個欄位的前面部分內容做為索引鍵來索引該欄位,來達到減小索引占用的儲存空間和提高索引訪問的效率。當然,字首索引的功能僅僅適用於字段字首比較隨機重複性很小的字段。

3、query 的索引選擇

在有些場景下,我們的 query 由於存在多個過濾條件,而這多個過濾條件可能會存在於兩個或者更多的索引中。在這種場景下,mysql query optimizer 一般情況下都能夠根據系統的統計資訊選擇出乙個針對該 query 最優的索引完成查詢,但是在有些情況下,可能是由於我們的系統統計資訊的不夠準確完整,也可能是 mysql query optimizer 自身功能的缺陷,會造成他並沒有選擇乙個真正最優的索引而選擇了其他查詢效率較低的索引。在這種時候,我們就不得不通過認為干預,在 query 中增加 hint 提示 mysql query optimizer 告訴他該使用哪個索引而不該使用哪個索引,或者通過調整查詢條件來達到相同的目的。

4、下面是我對於選擇合適索引的幾點建議,並不一定在任何場景下都合適,但在大多數場景下還是比較適用的。

對於單鍵索引,盡量選擇針對當前 query 過濾性更好的索引;

在選擇組合索引的時候,當前 query 中過濾性最好的字段在索引字段順序中排列越靠前越好;

在選擇組合索引的時候,盡量選擇可以能夠包含當前 query 的 where 子句中更多字段的索引;

盡可能通過分析統計資訊和調整 query 的寫法來達到選擇合適索引的目的而減少通過使用hint 人為控制索引的選擇,因為這會使後期的維護成本增加,同時增加維護所帶來的潛在風險。

5、mysql 中索引的限制

下面列出了目前 mysql 中索引使用相關的限制。

1. myisam 儲存引擎索引鍵長度總和不能超過 1000 位元組;

2. blob 和 text 型別的列只能建立字首索引;

3. 過濾字段使用了函式運算後(如 abs(column)),mysql 無法使用索引;

4. join 語句中 join 條件字段型別不一致的時候 mysql 無法使用索引;

5. 使用 like 操作的時候如果條件以萬用字元開始( 『%abc…』)mysql 無法使用索引;

SQL語句的優化(合理使用索引)

1.1 什麼時候使用索引?1 以查詢的關鍵字為基礎,表中行隨機排序 2 包含的列數 相對較少的表 3 表中大多數的查詢包含相對簡單的where從句 4 快取命中率低,而且也不用作業系統許可權 1.2 選擇什麼樣的索引列和表示式?1 where從句頻繁使用的關鍵字 2 重複性少的關鍵字 3 避免使用頻...

效能優化 合成層

1.提公升移動或漸變元素的繪製層 繪製並非總是在記憶體中的單層畫面裡完成的。實際上,瀏覽器在必要時將會把一幀畫面繪製成多層畫面,然後將這若干層畫面合併成一張顯示到螢幕上。通過渲染層提公升可以減小繪製區域,我們可以用除錯工具檢視到繪製層 在頁面中新建乙個渲染層最好的方式就是使用 will change...

python反素數演算法優化 合數

python中判斷乙個數是不是質數2020 12 18 16 57 27 首先說明,內容結合了其他人的程式設計想法 和其他學習平台學習的思路,本人只是將解法記錄下來方便自己之後查閱。由於本人學藝不精,如果有錯誤還請大家見諒並指出,謝謝。質數是除了1和它本身再無其他的因數,例如5。在數學上與質數相對的...