Mysql單錶查詢優化

2021-08-20 08:48:44 字數 3163 閱讀 3909

原文url:

我最近碰到了很多效能很糟糕的mysql單錶查詢。原因很簡單:索引建立得不正確,導致執行計畫的效能低下。下面是一些能幫助你優化單錶查詢效能的要點。

索引主要做3件事:過濾(filter),排序或分組(sort/group),覆蓋(cover)。前兩個沒什麼好說的,但並不是每個人都知道什麼叫「覆蓋索引」。事實上這是個很簡單的東西。

乙個基本查詢的工作流如下:

1. 使用索引以查詢匹配的記錄,並得到資料的指標。

2. 使用相關資料的指標。

3. 返回查詢到的記錄。

當可以使用覆蓋索引時,索引將會覆蓋查詢中的所有字段,因此第二步將會被跳過,於是查詢流程就變成了下面這樣:

1. 使用索引以查詢匹配的記錄

2. 返回查詢到的記錄。

大部分情況下,索引都比較小,可以載入在記憶體中,而資料很大,無法全部存放在記憶體裡:當使用覆蓋索引時,可以避免很多的磁碟操作,因此對效能也會有極大的改善。

下面讓我們來看一些常見的查詢案例。

這是最基本的情景:

[sql] 

view plain

copy

select

* from

t where

c = 100  

毫無疑問這種情況下,要給c欄位建立索引。要注意的是,如果查詢條件不夠精確(if the criteria is not selective enough,這句話我不理解),優化器很可能會選擇全表查詢,因為這樣有可能性能更好。

這種單個等於查詢也包括只查詢部分字段,而不是所有字段,如:

[sql] 

view plain

copy

select

c1, c2 

from

t where

c = 100  

這裡應該建立乙個(c,c1,c2)的索引,因為這樣是覆蓋索引。注意不是建立(c1,c2,c)!這同樣也是覆蓋索引,但是對過濾沒什麼幫助(記住mysql索引的最左原則)。

[sql] 

view plain

copy

select

* from

t where

c = 100 

andd = 

'xyz'

這種情況也很容易優化:建立索引(c,d)或(d,c)。

最常見的錯誤是建立兩個索引:乙個是c,乙個是d。儘管mysql根據index_merge演算法能同時使用這兩個索引,但這樣依然是糟糕的選擇(詳情參見以下幾篇文章:

[sql] 

view plain

copy

select

* from

t where

c > 100 

andd = 

'xyz'

這種情況我們必須要小心,因為只要有一列使用了不等於計算,那麼它將阻止其他列使用索引。

因此我們需要建立乙個(d,c)的索引,這時候c和d兩個條件都會走索引,這也是我們想要的結果。

而如果我們建立的是(c,d)索引,則只有c列的索引會被利用,這樣效率會比較低。

因此,索引中字段的順序對於這種等於/不等於並存的查詢有極大的影響。

[sql] 

view plain

copy

select

* from

t where

c > 100 

andb < 10 

andd = 

'xyz'

這裡有兩個不等於,前面已經說了不等於會終止索引查詢,因此我們不可能做到b、c、d都被索引覆蓋(

注釋1)。因此我們必須要做出決定,到底是建立索引(d,b)還是索引(d,c)?

在不知道表裡具體資料的情況下,建立上面任何一種都無所謂,最關鍵的是,一定要把等於條件(在這裡是d)所在列,放在索引的最左側。

注釋1:事實上還是有一種「曲線救國」的方法,能同時滿足所有條件,即按照欄位b分割槽(partition on b),然後建立索引(d,c),或按照欄位c分割槽(partition onc),然後建立索引(d,b)。這個的細節已經超出了本文的討論範圍,不過這也是這種情況下的一種解決方法。

[sql] 

view plain

copy

select

* from

t where

c = 100 

andd = 

'xyz'

order

byb  

就像第一節中寫的那樣,索引可以過濾、排序,因此這個查詢很容易優化。不過和不等於類似,我們對於索引中字段的順序必須足夠小心:

要求是先過濾後排序

。根據上面「先過濾後排序」的要求可知,(c,d,b)或(d,c,b)是不錯的選擇;而(b,c,d)或(b,d,c)則比較糟糕,因為他們只排序,不過濾。

如果是下面這種情況:

[sql] 

view plain

copy

select

c1, c2 

from

t where

c = 100 

andd = 

'xyz'

order

byb  

我們可以建立乙個集過濾、排序、覆蓋於一體的索引:(c,d,b,c1,c2)。

常見的情況有2種。下面是情況一(不等於、等於、排序都有):

[sql] 

view plain

copy

select

* from

t where

c > 100 

andd = 

'xyz'

order

byb  

這種情況有兩種思路:(d,b)或(d,c)。至於哪種效率更高,這取決於你的資料,需要具體情況具體分析。

情況二如下(只有不等於和排序):

[sql] 

view plain

copy

select

* from

t where

c > 100 

order

byb  

這種情況沒有等於條件,因此b和c只能選一種,具體選哪一種同樣和你的資料有關。通常情況下,選過濾的會多一些(即c欄位)。本文並沒有包含所有的情況,但同樣指出了一些你必須要小心的地方。今後,我會列舉乙個看起來十分複雜的例子,不過只要你把這篇文章看懂了,它其實很簡單。

MySQL 單錶查詢

1 基本資料記錄查詢 列出表的所有字段 select field1,field2.fieldn from tablename 2 符號的使用 select from tablename 其中,符號 表示所有欄位名 tablename 引數表示表的名稱。3 條件資料記錄查詢 select field1...

mysql 單 索引 mysql 單錶索引優化

建表語句 create table if not exists article id int 10 unsigned not null primary key auto increment,author id int 10 unsigned not null,category id int 10 u...

mysql 索引 單錶優化

索引分析 type 為 all using filesort 自己進行了排序沒有用到索引 檢視 article 表有什麼索引 show index from article 只有乙個主鍵 建立索引 where 後面的字段需要建索引,但不一定必須建。嘗試著去建 alter table 表名 add i...