高效能的索引策略(上)

2022-07-26 19:54:14 字數 2925 閱讀 5203

高效的選擇和使用索引有很多方式,其中有些事針對特殊案例的優化方法,有些則是針對特定行為的優化,使用哪個索引,以及如何評估選擇不同索引效能影響的技巧,則需要持續不斷的練習。接下來將介紹如何高效的使用索引。

獨立的列

我們通常會看到一些查詢不當的使用索引,或者是的mysql無法使用已有的索引。如果查詢中的列不是獨立的,則mysql就不會使用索引。「獨立的列」是指索引列不能是表示式的一部分,也不能是函式的引數。

例如,下面的這個查詢無法使用actor_id的索引:

select actor_id from actor where actor_id +1 = 5;

憑肉眼很容易看出where 中的表示式其實等價於 actor_id=4 ,但是mysql無法自動解析這個方程式。這完全是使用者的行為。我們應該養成簡化where條件的習慣,始終將索引列單獨放在比較符合的一側。

下面是常見的錯誤:

select ... where to_days(current_date) - to_days(date_col) <=10;

字首索引和索引選擇性

有時候需要索引很長的字元列,這會讓索引編的大且慢。乙個策略是前面提到的模擬hash索引。蛋有時候這樣做還不夠,還可以做什麼呢?

通常可以索引開始的部分字串,這樣可以大大節約索引空間,從而提高索引效率。但是這樣也會降低索引的選擇性。索引的選擇性是指,不重複索引的值(也稱為基數)和資料表的記錄總數(#t)的比值,範圍從1/#t到1之間。索引的選擇性越高,則查詢效率越高,因為選擇性高的索引可以讓mysql在查詢時過濾掉更多的行。唯一索引的選擇性是1,這是最好的索引選擇性,效能也是最好的。

一般情況下某個列字首索引的選擇性也是足夠高的,足以滿足查詢效能。對於blob、text或者很長的varchar型別的列,必須使用字首索引,因為mysql不允許索引這些列的完整長度。

訣竅在於要選擇足夠長的字首以保證較高的選擇性,同時又不能太長(以便節約空間)。字首應該足夠長,以使得字首索引的選擇性接近於索引整個列,換句話說,字首的「基數」應該接近於完整列的「基數」。

為了決定字首的合適長度,需要找到最常見的值的列表,然後和最常見的字首列進行比較。

字首索引是一種能使索引更小,更快的有效辦法,但另一方面也有其缺點:mysql無法使用字首索引做order by 和group by 操作,也無法使用字首索引做覆蓋掃瞄。

乙個常見的場景是針對很懂行的十六進製制唯一id使用字首索引。在前面已經討論了很多有效的基數來儲存這類id資訊,但如果使用打包過的解決方案,因而無法修改儲存結構,那該怎麼辦?此時如果採用長度為8的淺醉索引通常能夠顯著的提公升效能,並且這種方法對引用層上完全透明。

有時候字尾索引也有用途(例如,找到摸個網域名稱的所有電子郵件位址)。mysql原生並不支援反向索引,但是可以吧字串反轉後儲存,並給予此建立字首索引。可以通過觸發器來維護這種索引。

多列索引

很多人對多列索引的理解都不夠。乙個常見的錯誤就是,為每個列建立獨立的索引,或者按照錯誤的順序建立多列索引。

我們會在稍後的章節中單獨討論索引列的順序問題。先來看第乙個問題,為每個列床架獨立的索引,從show create table 中很容易看到這種情況:

create table t(

c1 int,c2 int , c3 int ,key(c1),key(c2),key(c3)

這種索引策略,一般是由於人們聽到一些專家諸如「把where 條件裡面的列都建上索引」這樣模糊的建議導致的。實際上這個建議是非常錯誤的。這樣一來最好的情況也只能是「一星」索引,其效能比起真正最有效的索引可能差幾個數量級。有時如果無法設計出乙個「三星」索引,那麼不如忽略掉where 子句,集中精力優化索引列的順序,或者建立乙個全覆蓋索引。

在多個列上獎勵獨立的單列索引大部分情況下不能提高mysql的查詢效能。mysql5.0和更高的版本醫用了一種叫「索引合併」策略,一定程度上可以使用表上的多個單列索引來定位指定的行。更早版本的mysql只能使用其中某乙個單列索引,然而這種情況下沒有哪乙個獨立索引是非常有效的。例如在film_actor在字段film_id和actor_id上各有乙個單列索引。但是對於這個查詢where 條件,這兩個單列索引都不是好的選擇:

select film_id ,actor_id from film_actor where actor_id=1 or film_id =1;

在老的mysql版本中,mysql對於這個查詢是會使用全表掃瞄的,除非改寫成如下的兩個查詢union的方式:

select film_id ,actor_id from film_actor where actor_id=1

union all

select film_id ,actor_id from film_actor where film_id=1;

但是在mysql5.0 和更高的版本中,產線能夠同時使用者兩個單列索引進行掃公尺昂,並將結果進行合併。這種演算法有三個變種:or條件的聯合,and條件的相交,組合前面兩種情況的聯合及相交。下面的查詢就是使用了兩個索引掃瞄的聯合,通過explain中的extra列可以看出這點:

explain select film_id,actor_id from film_actor where actor_id=1 or film_id = 1 \g

mysql會使用這類技術優化負責的查詢,所以在某些語句的extra列中還可以看到巢狀操作。

當出現伺服器對多個索引做相交操作(通常有多個and條件),通常意味著需要乙個包含所有相關列的多個索引,而不是獨立的單列索引。

當伺服器需要對多個索引做聯合操作(通常有多個or條件),通常需要耗費大量的cpu和記憶體資源在演算法的緩衝,排序和合併的操作上。特別是當其中有些索引的選擇性不高。需要合併掃瞄返回大量資料的時候。

更重要的是,優化器不會吧這些成本算到「查詢成本」中,遊虎丘只關心隨機頁面讀取。這會使得查詢成本被低估,導致該執行計畫還不如直接走全表掃瞄。這樣做不但會消耗更多的cup和記憶體資源,還可能影響查詢的併發性,但如果是單獨魚腥這樣的查詢則往往會忽略對並發現的影響。通常來說,還不弱在mysql4.1或更早的時代一樣,將查詢改寫成union的方式往往會更好。

mysql 高效能索引策略

三 合適的 聯合 索引列順序 四 覆蓋索引和索引覆蓋 五 冗餘和重複索引 索引列不能是表示式的一部分,也不能是函式的引數。對於blob text或很長的varchar型別的列,必須使用字首索引,因為mysql不允許索引這些列的完整長度。字首索引一般適用於 like 查詢,無法使用字首索引做order...

3 3 高效能的索引策略

在進行查詢時,索引列不能是表示式的一部分,也不能是函式的引數,否則無法使用索引。例如下面的查詢不能使用 actor id 列的索引 select actor id from sakila.actor where actor id 1 5 在需要使用多個列作為條件進行查詢時,使用多列索引比使用多個單列...

mysql高效能索引 mysql高效能索引( )

在開發中,我們知道大多數應用的瓶頸在於sql語句的執行時耗,在這裡並不討論sql語句的安全,僅僅討論高效能sql語句,而與高效能sql語句緊密相連的就是傳說中的 索引。索引 一種工作在儲存引擎端的用於快速找到記錄的一種資料結構。mysql使用索引的方式是 先找到索引的值,再根據索引的值找到資料行。索...