最近作一個CRM系統,發現了慢查詢日誌裏記載了許多的慢sql,因而就對其進行了sql優化。在優化的過程當中,本身也概括整理了一些sql優化的方案。今天就來和你們聊聊。html
**一、慢查詢的分析**
常見的分析慢查詢sql的方法大概有三種:explain、show profile、trace 分析 sql優化器。本文主要介紹explain的方法去分析慢sql,其他兩種方法有興趣的同窗能夠去了解下。算法
**二、explain各參數解釋**
(1)explain語法:explain+「需分析的sql」;
例:我想分析「select * from table1 where b=500;」這條sql的執行效率,那麼直接在sql命令行下執行「expalin select * from table1 where b=500;」就能夠查看了;執行結果以下:
根據上圖,能夠看到有許多個字段,那這些字段分別有什麼意義呢?見下圖(紅框標出的爲重點關注字段):
其中各個字段又可能有多個不一樣的值,重點關注字段select_type、type、Extra的可能值以下圖所示:
其中,上圖中的「type」的值的查詢性能從上到下依次是最好到最差。sql
**三、經常使用sql優化方法**數據庫
(1)使用索引
你們對MySQL的索引應該不會感到陌生吧,數據量大的時候,最多見的加快查詢效率的方法那確定是加索引了,要知道MySQL的B+索引樹但是能在2~4層樹就能從上億的數據中提取出相關數據的,不加索引的話得進行上億次磁盤io查找(關於B+樹的具體原理,你們能夠參考下這篇文章,寫得很不錯,http://www.liuzk.com/410.html。索引雖好用,可是存在不少不走索引的狀況。下面列舉常見幾種不走索引的狀況,以及如何讓它走索引:
①對查詢條件使用函數
如:select * from table1 where date(c) ='2020-08-20';
應改成:select * from table1 where c>='2020-08-20 00:00:00' and c<='2020-08-20 23:59:59';
②隱式轉換:如把varchar類型當成int型去寫
例:select * from table1 where a=1000; (其中,a字段在數據庫中是varchar類型)
應改成:select * from table1 where a='1000';
③模糊查詢
如:select * from table1 where a like '%1111%';
應改成:select * from table1 where a like '1111%'; 但要看具體業務,可能不對
注意:select * from table1 where a like '%1111'; 這種也是不走索引的
能夠這麼理解:like 匹配是%在前面的都不走索引
④範圍查詢
如:select * from table1 where b>=1 and b <=2000; (這條數據查詢範圍過大,是全表掃描,優化器選擇不走索引)
應改成:select * from table1 where b>=1 and b <=1000;
select * from table1 where b>=1001 and b <=2000;
⑤計算操做 (但通常在代碼層面作操做,不多會在數據庫作操做)
如:select * from table1 where b-1 =1000;
應改成:select * from table1 where b =1000 + 1;
⑥OR 操做
若是條件中有OR,即便其中有條件帶索引也不會使用。換言之,就是要求使用的全部字段,都必須創建索引。因此除非每一個列都創建了索引,不然不建議使用OR,在多列OR中,能夠考慮用UNION 替換
如:select * from table1 where create_time = '2020-08-20 11:49:30' OR b > 854;
應改成:select * from table1 where create_time = '2020-08-20 11:49:30' UNION select * from table1 where b > 854;函數
(2)分頁優化
如:select * from table1 order by a limit 99000,10; (其中,a字段有索引)
但結果不走索引,緣由是掃描整個索引並查找到沒索引的行的成本比掃描全表的成本更高,因此優化器放棄使用索引。(關鍵是讓排序時返回的字段儘量少)
應改成:select * from table1 f inner join (select id from table1 order by a limit 99000,10)g on f.id = g.id;
或者 select * from table1 where id >= (select id from table1 order by a limit 99000,1) limit 10;oop
(3)鏈接查詢優化
在項目中,錶鏈接查詢是比較常見的,尤爲是一些統計模塊。錶鏈接有兩種算法,一種叫Nested-Loop Join 算法(簡稱NLJ),另外一種是Block Nested-Loop Join 算法(簡稱BNL)(關聯字段不存在索引時會使用到)。感興趣的小夥伴能夠深刻去了解下相關原理哦。咱們進行關聯查詢優化的方法是:儘可能讓 BNL變成 NLJ ,就是說在關聯字段上加上索引。性能
(4)order by和group by優化
①首先咱們要知道MySQL中有兩種排序方式,一種是經過有序索引直接返回有序數據(Extra字段:Using index);另外一種是經過 Filesort 進行的排序,不走索引(Extra字段:Using filesort);毫無疑問,確定是有序索引排序更快。
常見order by優化方法:
①在排序字段上添加索引
如:select c,id from table1 order by c; (c有索引)
②多個字段排序,能夠在多個排序字段上添加聯合索引來優化排序語句
如:select id,a,b from table1 order by a,b; (a,b是聯合索引)
注意:select id,a,b from table1 order by b,a; (a,b是聯合索引)此時不走索引,最左匹配前綴原則瞭解下
③對於先等值查詢再排序的語句,能夠經過在條件字段和排序字段添加聯合索引來優化
如:select id,a,b from table1 where a=1000 order by b; (a,b是聯合索引)
若是a,b不是聯合索引的話,即時有a索引,b索引,排序也不會走索引
④去掉沒必要要的返回字段
如:select * from table1 order by a,b; /* 根據a和b字段排序查出全部字段的值 */
應改成:select id,a,b from table1 order by a,b; /* 根據a和b字段排序查出id,a,b字段的值 */
不走索引緣由:掃描整個索引並查找到沒索引的行的成本比掃描全表的成本更高,
因此優化器放棄使用索引。
默認狀況,會對 group by 字段排序,所以group by優化方式與 order by 基本一致。優化
本篇文章就寫到這,大家工做中還有什麼好的優化sql的方法嗎?歡迎在評論區分享!spa