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

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

索引

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

注意

  • MYSQL一次查詢只能使用一個索引,這個說法是不正確的,MYSQL會在兩個索引列中,使用OR查詢的時候,進行索引合併(index_merge;Using union(col1,col2);),但這種創建索引會使得索引數據的膨脹,不建議使用。若是對多個字段使用索引,創建使用複合索引。
 

冗餘和重複索引

  • Mysql須要單獨維護重複的索引,而且優化查詢的時候也須要逐個進行考慮,這會影響性能。
  • 重複索引是指在相同的列上按照相同的順序建立的相同類型的索引。應該避免這樣建立重複索引,發現之後也應該當即移除。

例如:

  1. 在惟一限制和主鍵ID上創建索引,則是重複索引。Mysql的惟一限制和主鍵限制都是經過索引實現的。
  2. 若是建立了索引(A,B),再建立(A)索引,就是冗餘索引,這只是一個索引的前綴索引。
  3. 若是建立了索引(A,B),再建立(B,A)則不是冗餘索引。
  • 大多數狀況下都不須要冗餘索引,應該儘可能擴展已有的索引而不是建立新的索引。
  • 解決冗餘和重複索引的方法很簡單,刪除這些索引就能夠了。可使用Percona-Toolikt的pt-duplicate-key-checker檢查重複索引。
 

索引的優勢:

  1. 索引大大減小了服務器須要掃描的數據量。
  2. 索引能夠幫助服務器避免排序和臨時表。
  3. 索引能夠將隨機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;。

可以使用到索引的狀況

  1. desc select col1, col2, col3 from tab order by col1, col2, col3。
  2. desc select col1, col2 from tab where col1 = 1 order by col2。[有where的時候,前導列爲常數的狀況下能夠用索引]
  3. desc select * from tab where col1 < 4 order by col1 desc。
  4. desc select * from tab where col1 = 10 and col2 > 10 order by col2 desc。
  5. desc select * from tab where col1 < 10 and col2 > 20 order by col1 desc。

不可以使用索引的狀況

  1. desc select col1 from tab where col1 > 18 order by col2, col3。[col1是一個範圍查詢條件,而不是一個常數,沒法使用索引]
  2. desc select * from tab where col1 > 1 order by col2, col1。[排序鍵順序與索引中列順序不一致,沒法使用索引排序]
  3. desc select col1, col2 from tab order by col1 desc, col2 asc。[升序降序不一致,沒法使用索引排序]
  4. desc select col1, col2 from tab where col1 > 1 order by col2。[條件1是範圍查詢,沒法使用索引排序,具體和組合索引數據分佈有關係]
  5. desc select * from tab where col1 = 1234 or col2 = 4321 order by col2。[當你邏輯表達式爲OR時,使用索引排序會丟失,沒法使用索引排序]
  6. desc select * from tab where col1 = 123 and col2 in (1,2) order by col3。[IN查詢也是一種範圍查詢,沒法使用索引排序]
  7. 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]

使用鬆散索引掃描須要知足如下條件
  1. 查詢在單一表上。
  2. GROUP BY指定的全部列是索引的一個最左前綴,而且沒有其餘的列。好比:表tab1(col1,col2,col3,col4)上創建了索引(col1,col2,col3)。若是查詢包含「group by col1,col2」,那麼可使用鬆散索引掃描。可是「group by col2,col3」(不是最左前綴)和「group by col1,col2,col4」(col4字段不在索引中)沒法使用。
  3. 若是在選擇列表select list中存在彙集函數,只能使用MIN()和MAX()兩個聚合函數,而且指定的事同一個列(若是min()和max()同時存在),這一列必須在索引中,且緊跟着GROUP BY指定的列。好比:select col1,col2,min(col3),max(col3) from tab group by col1, col2。
  4. 若是查詢中存在除了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

 
 

優化建議

  1. JOIN。
    1. 在ON字段上加索引,而且字段類型必須是相同的,不然MYSQL沒法使用到它們的索引。
  2. 使用 ENUM 而不是 VARCHAR。
  3. 避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN ORDER BY RAND 等這樣的操做符。
  4. 把IP地址存成 UNSIGNED INT。
  5. 固定長度的表會更快。
  6. 垂直分割。
    1. 「垂直分割」是一種把數據庫中的表按列變成幾張表的方法,這樣能夠下降表的複雜度和字段的數目,從而達到優化的目的。
    2. 例一:在Users表中有一個字段是家庭地址,這個字段是可選字段,相比起,並且你在數據庫操做的時候除了我的信息外,你並不須要常常讀取或是改寫這個字段。那麼,爲何不把他放到另一張表中呢? 這樣會讓你的表有更好的性能,你們想一想是否是,大量的時候,我對於用戶表來講,只有用戶ID,用戶名,口令,用戶角色等會被常用。小一點的表老是會有好的性能。
    3. 示例二: 你有一個叫 「last_login」 的字段,它會在每次用戶登陸時被更新。可是,每次更新時會致使該表的查詢緩存被清空。因此,你能夠把這個字段放到另外一個表中,這樣就不會影響你對用戶ID,用戶名,用戶角色的不停地讀取了,由於查詢緩存會幫你增長不少性能。
    4. 另外,你須要注意的是,這些被分出去的字段所造成的表,你不會常常性地去Join他們,否則的話,這樣的性能會比不分割時還要差,並且,會是極數級的降低。
  7. 拆分大的 DELETE 或 INSERT 語句。
    1. 若是你須要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你須要很是當心,要避免你的操做讓你的整個網站中止相應。由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。
  8. 越小的列會越快。建議:布爾/枚舉:tinyint,日期與時間戳:timestamp或int,char/text/blob: 儘可能用符合實際長度的varchar(n),小數及貨幣:移位轉爲int 或 decimal,IP地址:int。
  9. 在新建臨時表時,若是一次性插入數據量很大,那麼可使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。
  10. 儘可能使用數字型字段,若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會 逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
  11. NOT IN、NOT EXISTS的相關子查詢能夠改用LEFT JOIN代替寫法。
  12. 能用UNION ALL就不要用UNION,需知道二者區別。
  13. 儘可能避免使用or,會致使數據庫引擎放棄索引進行全表掃描。
    1. SELECT * FROM t WHERE id = 1 OR id = 3
    2. 優化方式:能夠用union代替or。以下:
    3. SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3
  14. 可以用BETWEEN的就不要用IN。
  15. 可以用DISTINCT的就不用GROUP BY。
  16. 儘可能不要用SELECT INTO語句。SELECT INTO 語句會致使表鎖定,阻止其餘用戶訪問該表。
  17. UPDATE操做不要拆成DELETE操做+INSERT操做的形式,雖然功能相同,可是性能差異是很大的。
  18. 索引字段上進行運算會使索引失效。
 

數據表結構優化建議

根據數據分析建議來修改表結構,使之更符合數據存儲規範: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也有作索引列的話,那麼兩條均能使用到索引查詢。
相關文章
相關標籤/搜索