SQL性能優化
一、SQL的執行順序
順序:FROM——ON——JOIN——WHERE——GROUP BY——SUM、COUNT——HAVING——SELECT——DISTINCT——ORDER BY——LIMIT
與寫SQL的順序不同,SQL的執行順序并不是從select開始,而是從from開始
1、FROM:先去獲取from里面的表,拿到對應的數據,生成虛擬表1。
2、ON:對虛擬表1應用ON篩選,符合條件的數據生成虛擬表2。
3、JOIN:根據JOIN的類型去執行相對應的操作,獲取對應的數據,生成虛擬表3。
4、WHERE:對虛擬表3的數據進行條件過濾,符合記錄的數據生成虛擬表4。
5、GROUP BY:根據group by中的列,對虛擬表4進行數據分組操作,生成虛擬表5。
6、CUBE|ROLLUP(聚合函數使用):主要是使用相關的聚合函數,生成虛擬表6。
7、HAVING:對虛擬表6的數據過濾,生成虛擬表7,這個過濾是在where中無法完成的,同時count(expr)返回不為NULL的行數,而count(1)和count(*)是會返回包括NULL在內的行數。
8、SELECT:選擇指定的列,生成虛擬表8。
9、DISTINCT:數據去重,生成虛擬表9。
10、ORDER BY:對虛擬表9中的數據進行指定列的排序,生成虛擬表10。
11、LIMIT:取出指定行的記錄,生成虛擬表11,返回給查詢用戶。
以上是SQL各關鍵詞的執行順序,如果在一條SQL語句里面你沒有用到某個關鍵詞那就不會被執行了。理解SQL的邏輯執行順序對我們在實際寫SQL的過程中也會有幫助的。
二、執行計劃——EXPLAIN
執行計劃,是SQL在數據庫中執行時的表現情況,通常用于SQL性能分析,優化等場景。在MySQL使用 explain 關鍵字來查看SQL的執行計劃。
基本的語法:EXPLAIN(select * from table)
在常規SQL語句前面加上EXPLAIN即可
運行結果:
參數解釋:
1、id:數字越大越先執行,一樣大則從上往下執行,如果為NULL則表示是結果集,不需要用來查詢。
2、select_type:
simple:不需要union的操作或者是不包含子查詢的簡單select語句。
primary:需要union操作或者含有子查詢的select語句。
union:連接兩個select查詢,第一個查詢是dervied派生表,第二個及后面的表select_type都是union。
dependent union:與union一樣,出現在union 或union all語句中,但是這個查詢要受到外部查詢的影響。
union result:包含union的結果集。
subquery:除了from字句中包含的子查詢外,其他地方出現的子查詢都可能是subquery。
dependent subquery:與dependent union類似,表示這個subquery的查詢要受到外部表查詢的影響。
derived:from字句中出現的子查詢,也叫做派生表,其他數據庫中可能叫做內聯視圖或嵌套select。
3、table
表名,如果是用了別名,則顯示別名
4、type
依次從好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL,除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一個索引。
system:表中只有一行數據或者是空表。
const:使用唯一索引或者主鍵,返回記錄一定是1行記錄的等值where條件時,通常type是const。
eq_ref:出現在要連接過個表的查詢計劃中,驅動表只返回一行數據,且這行數據是第二個表的主鍵或者唯一索引,且必須為not null,唯一索引和主鍵是多列時,只有所有的列都用作比較時才會出現eq_ref。
ref:不像eq_ref那樣要求連接順序,也沒有主鍵和唯一索引的要求,只要使用相等條件檢索時就可能出現,常見與輔助索引的等值查找。
fulltext:全文索引檢索,要注意,全文索引的優先級很高,若全文索引和普通索引同時存在時,mysql不管代價,優先選擇使用全文索引。
ref_or_null:與ref方法類似,只是增加了null值的比較。實際用的不多。
unique_subquery:用于where中的in形式子查詢,子查詢返回不重復值唯一值。
index_subquery:用于in形式子查詢使用到了輔助索引或者in常數列表,子查詢可能返回重復值,可以使用索引將子查詢去重。
range:索引范圍掃描,常見于使用>,<,is null,between ,in ,like等運算符的查詢中。
index_merge:表示查詢使用了兩個以上的索引,最后取交集或者并集,常見and ,or的條件使用了不同的索引。
index:索引全表掃描,把索引從頭到尾掃一遍,常見于使用索引列就可以處理不需要讀取數據文件的查詢、可以使用索引排序或者分組的查詢。
all:這個就是全表掃描數據文件,然后再在server層進行過濾返回符合要求的記錄。
5、possible_keys:查詢可能使用到的索引。
6、key:查詢真正使用到的索引。
7、key_len:用于處理查詢的索引長度。
8、ref:常數等值查詢顯示const,連接查詢則顯示表的關聯字段。
9、rows:執行計劃中估算的掃描行數,不是精確值。
10、filtered:表示存儲引擎返回的數據在server層過濾后,剩下多少滿足查詢的記錄數量的比例。
11、extra:該字段信息較多,這里就不一一敘述了。
在實際的使用過程中我們需要重點去關注type、key、key_len、rows、extra這幾個參數,type要努力優化到range級別,all要盡量少的出現,在查詢的過程中要盡量使用索引,提高效率,在extra里面出現Using filesort, Using temporary是不太好的,要去優化提高性能。
三、優化TIPS
1、盡量少用select *
因為會增加不必要的消耗,select 后面直接加上需要的字段名。
2、IN 包含的值不應過多
IN本身這個操作消耗就比較高,如果IN里面是連續的數值,則可以用between代替,IN里面的字段如果是添加了索引,效率還是可以的,目前測試一萬以內還是可以,但是超過了結果可能會有點爆炸,不要問我為什么
3、in和exists、not in 和 not exists
exists以外層表為驅動表,先被訪問,適合于外表小而內表大的情況。
in則是先執行子查詢,適合外表大而內表小的情況,
一般情況是不推薦使用not in,因為效率非常低,
eg:
1)select * from table_a where table_a.id not in (select table_b.id from table_b)
2)select * from table_a left join table_b on table_a.id = table_b.id where table_b.id is null
語句2的效率是要高于語句1的,SQL的結果是獲取到在table_a中存在但是table_b中不存在的數據,如果直接用not in是不走索引的,而且在table_b比較大的時候效率會非常低,實際工作中我試了一下直接not in,然后數據達到一萬條的時候大概需要150S左右才能查出數據(感謝DBA和運維不殺之恩),我采取的方法是,先查出兩個表的交集,這樣得到的表會小很多,而且是用的in,效率會高很多,然后再用not in,最終的效果也是一樣,但是時間只要2.56S,然后采取語句2的關聯表來處理,時間縮短到了1.42S,基本上效率是比較高的,當然理想的是在1S內。
4、盡量少用or,同時盡量用union all 代替union
or兩邊的字段如果有不走索引的會導致整個的查詢不走索引,從而導致效率低下,這時可以使用union all或者union,而兩者的區別是union是將兩個結果合并之后再進行唯一性的過濾操作,效率會比union all低很多,但是union all需要兩個數據集沒有重復的數據。
5、分段和分頁查詢
在掃描行數較多的情況下可以采取分段查詢,循環遍歷,結果合并處理,
使用合理的分頁方式,在數據表量級逐漸增加的時候,limit分頁查詢的效率會降低。
1)select id,col from table limit 888888,1000
2)select id,col from table where id > 888887 limit 1000
取前一頁的最大行數的id,然后根據這個id來限制下一頁的起點。
6、不建議使用%前綴模糊查詢
like "%abc"和like "%abc%"會導致索引失效而進行全文搜索。
如果你還有什么比較好的優化tips歡迎分享!
</article>