天天看點

mysql group by 彙總 優化_Mysql查詢優化彙總 order by優化例子,group by優化例子,limit優化例子,優化建議...

Mysql查詢優化彙總 order by優化例子,group by優化例子,limit優化例子,優化建議

索引

索引是一種存儲引擎快速查詢記錄的一種資料結構。

注意

MYSQL一次查詢隻能使用一個索引,這個說法是不正确的,MYSQL會在兩個索引列中,使用OR查詢的時候,進行索引合并(index_merge;Using union(col1,col2);),但這種建立索引會使得索引資料的膨脹,不建議使用。如果對多個字段使用索引,建立使用複合索引。

備援和重複索引

Mysql需要單獨維護重複的索引,并且優化查詢的時候也需要逐個進行考慮,這會影響性能。

重複索引是指在相同的列上按照相同的順序建立的相同類型的索引。應該避免這樣建立重複索引,發現以後也應該立即移除。

例如:

在唯一限制和主鍵ID上建立索引,則是重複索引。Mysql的唯一限制和主鍵限制都是通過索引實作的。

如果建立了索引(A,B),再建立(A)索引,就是備援索引,這隻是一個索引的字首索引。

如果建立了索引(A,B),再建立(B,A)則不是備援索引。

大多數情況下都不需要備援索引,應該盡量擴充已有的索引而不是建立新的索引。

解決備援和重複索引的方法很簡單,删除這些索引就可以了。可以使用Percona-Toolikt的pt-duplicate-key-checker檢查重複索引。

索引的優點:

索引大大減少了伺服器需要掃描的資料量。

索引可以幫助伺服器避免排序和臨時表。

索引可以将随機I/O變為順序I/O。

CREATE TABLE `tab` (

`col1` int(11) DEFAULT NULL,

`col2` int(11) DEFAULT NULL,

`col3` int(11) DEFAULT NULL,

`col4` int(11) DEFAULT NULL,

`id` int(11) NOT NULL AUTO_INCREMENT,

PRIMARY KEY (`id`),

KEY `col1_2` (`col1`,`col2`,`col3`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

insert into tab (col1, col2, col3) values (1,2,3,1),(4,5,6,1),(4,5,6,2),(4,5,6,3),(4,5,6,4),(4,5,6,3),(4,5,6,3);

CREATE TABLE `tab_1` (

`col1` int(11) DEFAULT NULL,

`col2` int(11) DEFAULT NULL,

`col3` int(11) DEFAULT NULL,

`col4` int(11) DEFAULT NULL,

`id` int(11) NOT NULL AUTO_INCREMENT,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ORDER BY優化

在ORDER BY操作中,MYSQL隻有在排序條件不是一個查詢表達式的情況下才使用索引。

說明:表:tab,字段:col1,col2,col3,索引:INDEX(col1,col2,col3)。

在關聯表查詢(JOIN)中,order by第一張表會使用索引排序,如果是使用JOIN的話,由于優化器在優化時可能将第二個表當成第一張表即(RIGHT JOIN),那麼實際上無法使用索引,如:desc select t.col1 from tab t left join tab_1 t1 on t.id = t1.id order by t.col1;。

能夠使用到索引的情況

desc select col1, col2, col3 from tab order by col1, col2, col3。

desc select col1, col2 from tab where col1 = 1 order by col2。[有where的時候,前導列為常數的情況下可以用索引]

desc select * from tab where col1 < 4 order by col1 desc。

desc select * from tab where col1 = 10 and col2 > 10 order by col2 desc。

desc select * from tab where col1 < 10 and col2 > 20 order by col1 desc。

不能夠使用索引的情況

desc select col1 from tab where col1 > 18 order by col2, col3。[col1是一個範圍查詢條件,而不是一個常數,無法使用索引]

desc select * from tab where col1 > 1 order by col2, col1。[排序鍵順序與索引中列順序不一緻,無法使用索引排序]

desc select col1, col2 from tab order by col1 desc, col2 asc。[升序降序不一緻,無法使用索引排序]

desc select col1, col2 from tab where col1 > 1 order by col2。[條件1是範圍查詢,無法使用索引排序,具體群組合索引資料分布有關系]

desc select * from tab where col1 = 1234 or col2 = 4321 order by col2。[當你邏輯表達式為OR時,使用索引排序會丢失,無法使用索引排序]

desc select * from tab where col1 = 123 and col2 in (1,2) order by col3。[IN查詢也是一種範圍查詢,無法使用索引排序]

desc select * from tab where col1 = 123 order by col2, col4。[ORDER BY子句中引用了一個不在索引中的列,col4,無法使用索引排序]

LIMIT優化

對limit分頁問題的性能優化方法-延遲關聯

推薦使用“延遲關聯”的方法來優化排序操作,何為“延遲關聯”:通過使用覆寫索引查詢傳回需要的主鍵,然後再根據主鍵關聯原表獲得需要的資料來加速分頁查詢。

我們都知道,利用了索引查詢的語句中如果隻包含了那個索引列(覆寫索引),那麼這種情況會查詢很快。因為利用索引查找有優化算法,且資料就在查詢索引上面,不用再去找相關的資料位址了,這樣節省了很多時間。

例如

select * from ummor_user_detail ud order by user_id limit 300000,20 ;

優化寫法1

select * from ummor_user_detail ud where user_id >= (select user_id from ummor_user_detail order by user_id limit 300000,1) limit 20 ;

優化寫法2

select * from ummor_user_detail as a join (select user_id from ummor_user_detail order by user_id limit 300000,20 ) as b on a.user_id = b.user_id;

GROUP BY

MYSQL有三種索引掃描方式完成GROUP BY操作,分别是松散索引掃描和緊湊索引掃描以及臨時表實作GROUP BY。

在松散索引掃描下,分組操作和範圍預測(如果有的話)一起執行完成的。

在緊湊索引掃描下,先對索引執行範圍掃描(range scan),再對結果元祖進行分組。

GROUP BY優化

desc select * from tab where col1 > 1 group by col1, col2, col3, col4。[松散索引掃描]

desc select * from tab where col2 > 1 group by col1, col3。[緊湊索引掃描,必須加where,在查詢中存在常量相等的where條件字段(索引中的字段,且該字段在GROUP BY指定的字段的前面或者中間)]

desc select * from tab where col1 > 1 group by col2, col3。

desc select * from tab where col1 > 1 group by col3, col4。[臨時表實作GROUP BY]

1. 松散索引掃描[Loose Index San]

使用松散索引掃描需要滿足以下條件

查詢在單一表上。

GROUP BY指定的所有列是索引的一個最左字首,并且沒有其他的列。比如:表tab1(col1,col2,col3,col4)上建立了索引(col1,col2,col3)。如果查詢包含“group by col1,col2”,那麼可以使用松散索引掃描。但是“group by col2,col3”(不是最左字首)和“group by col1,col2,col4”(col4字段不在索引中)無法使用。

如果在選擇清單select list中存在聚集函數,隻能使用MIN()和MAX()兩個聚合函數,并且指定的事同一個列(如果min()和max()同時存在),這一列必須在索引中,且緊跟着GROUP BY指定的列。比如:select col1,col2,min(col3),max(col3) from tab group by col1, col2。

如果查詢中存在除了group by指定的列之外的索引其他部分,那麼必須以常量的形式出現(除了min()和max()兩個聚集函數)。比如:select col1, col3 from tab group by col1, col2不能使用松散索引掃描。而select col1, col3 from tab where col3 = 3 group by col1, col2可以使用松散索引掃描。

2. 緊湊索引掃描(Tight Index Scan)

緊湊索引掃描可能是全索引掃描或者範圍索引掃描,取決于查詢條件。

緊湊索引掃描實作GROUP BY和松散索引掃描的差別主要在于他需要在掃描索引的時候,讀取所有滿足條件的索引鍵,然後再根據讀取的資料來完成GROUP BY 操作得到相應結果。

3. 使用臨時表實作GROUP BY

優化建議

JOIN。

在ON字段上加索引,并且字段類型必須是相同的,否則MYSQL無法使用到它們的索引。

使用 ENUM 而不是 VARCHAR。

避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN ORDER BY RAND 等這樣的操作符。

把IP位址存成 UNSIGNED INT。

固定長度的表會更快。

垂直分割。

“垂直分割”是一種把資料庫中的表按列變成幾張表的方法,這樣可以降低表的複雜度和字段的數目,進而達到優化的目的。

例一:在Users表中有一個字段是家庭位址,這個字段是可選字段,相比起,而且你在資料庫操作的時候除了個人資訊外,你并不需要經常讀取或是改寫這個字段。那麼,為什麼不把他放到另外一張表中呢? 這樣會讓你的表有更好的性能,大家想想是不是,大量的時候,我對于使用者表來說,隻有使用者ID,使用者名,密碼,使用者角色等會被經常使用。小一點的表總是會有好的性能。

示例二: 你有一個叫 “last_login” 的字段,它會在每次使用者登入時被更新。但是,每次更新時會導緻該表的查詢緩存被清空。是以,你可以把這個字段放到另一個表中,這樣就不會影響你對使用者ID,使用者名,使用者角色的不停地讀取了,因為查詢緩存會幫你增加很多性能。

另外,你需要注意的是,這些被分出去的字段所形成的表,你不會經常性地去Join他們,不然的話,這樣的性能會比不分割時還要差,而且,會是極數級的下降。

拆分大的 DELETE 或 INSERT 語句。

如果你需要在一個線上的網站上去執行一個大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,别的操作都進不來了。

越小的列會越快。建議:布爾/枚舉:tinyint,日期與時間戳:timestamp或int,char/text/blob: 盡量用符合實際長度的varchar(n),小數及貨币:移位轉為int 或 decimal,IP位址:int。

在建立臨時表時,如果一次性插入資料量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果資料量不大,為了緩和系統表的資源,應先create table,然後insert。

盡量使用數字型字段,若隻含數值資訊的字段盡量不要設計為字元型,這會降低查詢和連接配接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連接配接時會 逐個比較字元串中每一個字元,而對于數字型而言隻需要比較一次就夠了。

NOT IN、NOT EXISTS的相關子查詢可以改用LEFT JOIN代替寫法。

能用UNION ALL就不要用UNION,需知道兩者差別。

盡量避免使用or,會導緻資料庫引擎放棄索引進行全表掃描。

SELECT * FROM t WHERE id = 1 OR id = 3

優化方式:可以用union代替or。如下:

SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3

能夠用BETWEEN的就不要用IN。

能夠用DISTINCT的就不用GROUP BY。

盡量不要用SELECT INTO語句。SELECT INTO 語句會導緻表鎖定,阻止其他使用者通路該表。

UPDATE操作不要拆成DELETE操作+INSERT操作的形式,雖然功能相同,但是性能差别是很大的。

索引字段上進行運算會使索引失效。

資料表結構優化建議

根據資料分析建議來修改表結構,使之更符合資料存儲規範:PROCEDURE ANALYSE(1)。

如:select * from tab PROCEDURE ANALYSE(1);分析出每個字段。

編寫MYSQL習慣

1. SELECT * from TABLE1 和 SELECT * FROM TABLE1

上面的兩條SQL語句對于查詢緩沖是完全不同的SELECT。而且查詢緩沖并不自動處理空格,是以,在寫SQL語句時,應盡量減少空格的使用,尤其是在SQL首和尾的空格(因為,查詢緩沖并不自動截取首尾空格)。

2. 關鍵字大寫。如:SELECT,LIKE,OR。

例子:

desc select col1, col2 from tab where col1 = 1 or col2 = 1; # index col1, index col2 索引合并,不建議

desc select * from tab where col1 = 1 or col2 = 2; # index col1, index col2,使用col1索引,索引合并,不建議使用

desc select col1, col2 from tab where col1 = 1 or col2 = 1; # index col1 col3, index col2 沒有使用到索引,不是獨立索引列

desc select * from tab where col1 = 1 or col2 = 2; # index col1 col3, index col2 沒有使用到索引,不是獨立索引列

desc select col1,col2 from tab where col1 = 1 or col2 = 1; # index col1, col2 使用到了索引

desc select * from tab where col1 = 1 or col2 = 1; # index col1, col2 沒有使用到索引

對比下面

desc

select * from tab where col1 = 1

union all

select * from tab where col2 = 2; # 使用這個政策是第一條語句能夠使用到索引,而第二條不行,這樣就有一半的資料還是能夠使用到索引,而不是整條語句使用不到索引。如果col2也有做索引列的話,那麼兩條均能使用到索引查詢。