SQL性能優(yōu)化
一柳击、SQL的執(zhí)行順序
順序:FROM——ON——JOIN——WHERE——GROUP BY——SUM旦签、COUNT——HAVING——SELECT——DISTINCT——ORDER BY——LIMIT
與寫SQL的順序不同负芋,SQL的執(zhí)行順序并不是從select開始散庶,而是從from開始
1不傅、FROM:先去獲取from里面的表茴厉,拿到對應(yīng)的數(shù)據(jù)泽台,生成虛擬表1。
2矾缓、ON:對虛擬表1應(yīng)用ON篩選怀酷,符合條件的數(shù)據(jù)生成虛擬表2。
3嗜闻、JOIN:根據(jù)JOIN的類型去執(zhí)行相對應(yīng)的操作胰坟,獲取對應(yīng)的數(shù)據(jù),生成虛擬表3泞辐。
4笔横、WHERE:對虛擬表3的數(shù)據(jù)進(jìn)行條件過濾,符合記錄的數(shù)據(jù)生成虛擬表4咐吼。
5吹缔、GROUP BY:根據(jù)group by中的列,對虛擬表4進(jìn)行數(shù)據(jù)分組操作锯茄,生成虛擬表5厢塘。
6、CUBE|ROLLUP(聚合函數(shù)使用):主要是使用相關(guān)的聚合函數(shù)肌幽,生成虛擬表6晚碾。
7、HAVING:對虛擬表6的數(shù)據(jù)過濾喂急,生成虛擬表7格嘁,這個(gè)過濾是在where中無法完成的,同時(shí)count(expr)返回不為NULL的行數(shù)廊移,而count(1)和count(*)是會返回包括NULL在內(nèi)的行數(shù)糕簿。
8、SELECT:選擇指定的列狡孔,生成虛擬表8懂诗。
9、DISTINCT:數(shù)據(jù)去重苗膝,生成虛擬表9殃恒。
10、ORDER BY:對虛擬表9中的數(shù)據(jù)進(jìn)行指定列的排序,生成虛擬表10离唐。
11隆嗅、LIMIT:取出指定行的記錄,生成虛擬表11侯繁,返回給查詢用戶胖喳。
以上是SQL各關(guān)鍵詞的執(zhí)行順序,如果在一條SQL語句里面你沒有用到某個(gè)關(guān)鍵詞那就不會被執(zhí)行了贮竟。理解SQL的邏輯執(zhí)行順序?qū)ξ覀冊趯?shí)際寫SQL的過程中也會有幫助的丽焊。
二、執(zhí)行計(jì)劃——EXPLAIN
執(zhí)行計(jì)劃咕别,是SQL在數(shù)據(jù)庫中執(zhí)行時(shí)的表現(xiàn)情況,通常用于SQL性能分析,優(yōu)化等場景技健。在MySQL使用 explain 關(guān)鍵字來查看SQL的執(zhí)行計(jì)劃。
基本的語法:EXPLAIN(select * from table)
在常規(guī)SQL語句前面加上EXPLAIN即可
運(yùn)行結(jié)果:
參數(shù)解釋:
1惰拱、id:數(shù)字越大越先執(zhí)行雌贱,一樣大則從上往下執(zhí)行,如果為NULL則表示是結(jié)果集偿短,不需要用來查詢欣孤。
2、select_type:
simple:不需要union的操作或者是不包含子查詢的簡單select語句昔逗。
primary:需要union操作或者含有子查詢的select語句降传。
union:連接兩個(gè)select查詢,第一個(gè)查詢是dervied派生表勾怒,第二個(gè)及后面的表select_type都是union婆排。
dependent union:與union一樣,出現(xiàn)在union 或union all語句中笔链,但是這個(gè)查詢要受到外部查詢的影響段只。
union result:包含union的結(jié)果集。
subquery:除了from字句中包含的子查詢外鉴扫,其他地方出現(xiàn)的子查詢都可能是subquery赞枕。
dependent subquery:與dependent union類似,表示這個(gè)subquery的查詢要受到外部表查詢的影響幔妨。
derived:from字句中出現(xiàn)的子查詢鹦赎,也叫做派生表谍椅,其他數(shù)據(jù)庫中可能叫做內(nèi)聯(lián)視圖或嵌套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只可以用到一個(gè)索引。
system:表中只有一行數(shù)據(jù)或者是空表鸳址。
const:使用唯一索引或者主鍵瘩蚪,返回記錄一定是1行記錄的等值where條件時(shí),通常type是const稿黍。
eq_ref:出現(xiàn)在要連接過個(gè)表的查詢計(jì)劃中募舟,驅(qū)動表只返回一行數(shù)據(jù),且這行數(shù)據(jù)是第二個(gè)表的主鍵或者唯一索引闻察,且必須為not null拱礁,唯一索引和主鍵是多列時(shí),只有所有的列都用作比較時(shí)才會出現(xiàn)eq_ref辕漂。
ref:不像eq_ref那樣要求連接順序呢灶,也沒有主鍵和唯一索引的要求,只要使用相等條件檢索時(shí)就可能出現(xiàn)钉嘹,常見與輔助索引的等值查找鸯乃。
fulltext:全文索引檢索,要注意跋涣,全文索引的優(yōu)先級很高缨睡,若全文索引和普通索引同時(shí)存在時(shí),mysql不管代價(jià)陈辱,優(yōu)先選擇使用全文索引奖年。
ref_or_null:與ref方法類似,只是增加了null值的比較沛贪。實(shí)際用的不多陋守。
unique_subquery:用于where中的in形式子查詢震贵,子查詢返回不重復(fù)值唯一值。
index_subquery:用于in形式子查詢使用到了輔助索引或者in常數(shù)列表水评,子查詢可能返回重復(fù)值猩系,可以使用索引將子查詢?nèi)ブ亍?/p>
range:索引范圍掃描,常見于使用>,<,is null,between ,in ,like等運(yùn)算符的查詢中中燥。
index_merge:表示查詢使用了兩個(gè)以上的索引寇甸,最后取交集或者并集,常見and 疗涉,or的條件使用了不同的索引幽纷。
index:索引全表掃描,把索引從頭到尾掃一遍博敬,常見于使用索引列就可以處理不需要讀取數(shù)據(jù)文件的查詢友浸、可以使用索引排序或者分組的查詢。
all:這個(gè)就是全表掃描數(shù)據(jù)文件偏窝,然后再在server層進(jìn)行過濾返回符合要求的記錄收恢。
5、possible_keys:查詢可能使用到的索引祭往。
6伦意、key:查詢真正使用到的索引。
7硼补、key_len:用于處理查詢的索引長度驮肉。
8、ref:常數(shù)等值查詢顯示const已骇,連接查詢則顯示表的關(guān)聯(lián)字段离钝。
9、rows:執(zhí)行計(jì)劃中估算的掃描行數(shù)褪储,不是精確值卵渴。
10、filtered:表示存儲引擎返回的數(shù)據(jù)在server層過濾后鲤竹,剩下多少滿足查詢的記錄數(shù)量的比例浪读。
11、extra:該字段信息較多辛藻,這里就不一一敘述了碘橘。
在實(shí)際的使用過程中我們需要重點(diǎn)去關(guān)注type、key吱肌、key_len痘拆、rows、extra這幾個(gè)參數(shù)岩榆,type要努力優(yōu)化到range級別错负,all要盡量少的出現(xiàn)坟瓢,在查詢的過程中要盡量使用索引勇边,提高效率犹撒,在extra里面出現(xiàn)Using filesort, Using temporary是不太好的,要去優(yōu)化提高性能粒褒。
三识颊、優(yōu)化TIPS
1、盡量少用select *
因?yàn)闀黾硬槐匾南霓确兀瑂elect 后面直接加上需要的字段名祥款。
2、IN 包含的值不應(yīng)過多
IN本身這個(gè)操作消耗就比較高月杉,如果IN里面是連續(xù)的數(shù)值刃跛,則可以用between代替,IN里面的字段如果是添加了索引苛萎,效率還是可以的桨昙,目前測試一萬以內(nèi)還是可以,但是超過了結(jié)果可能會有點(diǎn)爆炸腌歉,不要問我為什么
3蛙酪、in和exists、not in 和 not exists
exists以外層表為驅(qū)動表翘盖,先被訪問桂塞,適合于外表小而內(nèi)表大的情況。
in則是先執(zhí)行子查詢馍驯,適合外表大而內(nèi)表小的情況阁危,
一般情況是不推薦使用not in,因?yàn)樾史浅5停?/p>
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的結(jié)果是獲取到在table_a中存在但是table_b中不存在的數(shù)據(jù)欲芹,如果直接用not in是不走索引的,而且在table_b比較大的時(shí)候效率會非常低吟吝,實(shí)際工作中我試了一下直接not in菱父,然后數(shù)據(jù)達(dá)到一萬條的時(shí)候大概需要150S左右才能查出數(shù)據(jù)(感謝DBA和運(yùn)維不殺之恩),我采取的方法是剑逃,先查出兩個(gè)表的交集浙宜,這樣得到的表會小很多,而且是用的in蛹磺,效率會高很多粟瞬,然后再用not in,最終的效果也是一樣萤捆,但是時(shí)間只要2.56S裙品,然后采取語句2的關(guān)聯(lián)表來處理俗批,時(shí)間縮短到了1.42S,基本上效率是比較高的市怎,當(dāng)然理想的是在1S內(nèi)岁忘。
4、盡量少用or区匠,同時(shí)盡量用union all 代替union
or兩邊的字段如果有不走索引的會導(dǎo)致整個(gè)的查詢不走索引干像,從而導(dǎo)致效率低下,這時(shí)可以使用union all或者union驰弄,而兩者的區(qū)別是union是將兩個(gè)結(jié)果合并之后再進(jìn)行唯一性的過濾操作麻汰,效率會比union all低很多,但是union all需要兩個(gè)數(shù)據(jù)集沒有重復(fù)的數(shù)據(jù)戚篙。
5五鲫、分段和分頁查詢
在掃描行數(shù)較多的情況下可以采取分段查詢,循環(huán)遍歷岔擂,結(jié)果合并處理位喂,
使用合理的分頁方式,在數(shù)據(jù)表量級逐漸增加的時(shí)候智亮,limit分頁查詢的效率會降低忆某。
1)select id,col from table limit 888888,1000
2)select id,col from table where id > 888887 limit 1000
取前一頁的最大行數(shù)的id,然后根據(jù)這個(gè)id來限制下一頁的起點(diǎn)阔蛉。
6弃舒、不建議使用%前綴模糊查詢
like "%abc"和like "%abc%"會導(dǎo)致索引失效而進(jìn)行全文搜索。
如果你還有什么比較好的優(yōu)化tips歡迎分享状原!
</article>