SQL優化總結
SQL優化重要性
在提升系統性能時,SQL優化是成本最低的,卻是效果最佳的,如果整個專案在SQL上優化的很優秀,系統性能會有質的跨越,又能讓老闆省下不少成本開支。
- 優化成本:硬體>系統配置>表結構>SQL及索引。
- 優化效果:硬體<系統配置<表結構<SQL及索引。
原則
- 減少資料訪問
- 返回更少的資料
- 減少互動次數
- 利用更多資源
SQL執行順序
理解SQL優化原理 ,首先要搞清楚SQL執行順序:1. SELECT 2. DISTINCT <select_list> 3. FROM <left_table> 4. <join_type>JOIN <right_table> 5. ON <join_condition> 6. WHERE <where_condition> 7. GROUP BY <group_by_list> 8. HAVING <having_condition> 9. ORDER BY <order_by_condition> 10.LIMIT <limit_number>
SQL優化策略
適用於資料量較大的業務場景,資料量不大的、或者可預知系統最終資料量不會太大的沒必要畫蛇添足。
避免不走索引的場景
模糊查詢
模糊查詢,避免前萬用字元 %,這會讓資料庫引擎放棄索引而進行全表掃描,在搜尋詞後面使用不影響。
SELECT sid FROM tb WHERE sname like '%周%'
如需求使用在搜尋詞前面匹配,可以考慮:
使用MySQL內建函式INSTR(str,substr) 來匹配,作用類似於java中的indexOf(),查詢字串出現的角標位置
使用FullText全文索引,用match against 檢索
資料量較大的情況,建議引用ElasticSearch、solr
IN 和 NOT IN
會導致引擎走全表掃描,避免使用
SELECT sname FROM tb WHERE sid IN ( 1, 2, 3 );
優化:
SELECT sname FROMtb WHERE sin BETWEEN 1 AND 3
IN 子查詢:
select * from A where A.id in (select id from B);
優化
select * from A where exists (select * from B where B.id = A.id);
條件或 OR
SELECT sname FROM tb WHERE sid = 1 OR sid = 5;
優化:
SELECT sname FROM tb WHERE sid = 1 UNION SELECT sname FROM tb WHERE sid = 5
判斷 null
SELECT * FROM tb WHERE score IS NULL;
優化:為欄位設定預設值,用預設值來判斷,例如0
SELECT * FROM tb WHERE score = 0;
where 條件
避免等號左邊進行函式、表示式操作,儘量移到右邊
-- 全表掃描 SELECT * FROM tb WHERE score/10 = 9 -- 走索引 SELECT * FROM tb WHERE score = 10*9
where 1 = 1 避免使用
通過程式碼或者動態SQL判斷,有條件就 where條件,沒條件就不要where
不等 != 或<>
使用索引列作為條件進行查詢時,需要避免使用<>或者!=。如確實業務需要,使用到不等於符號,需要在重新評估索引建立。
型別轉換
避免欄位型別的隱式轉換,如給varchar賦值型別為數值,涉及隱式型別轉換,造成不能正確走索引
select col1 from table where col_varchar=123;
排序 ORDER BY
order by 條件要與 where 的條件一致,不然不走索引
-- 不走age索引 SELECT * FROM t order by age; -- 走age索引 SELECT * FROM t where age > 0 order by age;
SELECT的其他優化
避免SELECT *
使用select * 取出全部列,會讓優化器無法完成索引覆蓋掃描這類優化,會影響優化器對執行計劃的選擇,也會增加網路頻寬消耗,更會帶來額外的I/O,記憶體和CPU消耗。
建議提出業務實際需要的列數,指定列名以取代select *
避免不確定結果的函式
now()、rand()、sysdate()、current_user()等不確定結果的函式很容易導致主庫與從庫相應的資料不一致。另外不確定值的函式,產生的SQL語句無法利用query cache。
關聯查詢
小表在前,大表在後
在MySQL中,執行 from 後的表關聯查詢是從左往右執行的(Oracle相反),第一張表會涉及到全表掃描,所以將小表放在前面,先掃小表,掃描快效率較高,在掃描後面的大表,或許只掃描大表的前100行就符合返回條件並return了。
別名
多表查詢,使用別名帶上列(列上加表字首),減少解析的時間並減少哪些友列名歧義引起的語法錯誤
優化 GROUP BY
預設情況下,MySQL 會對GROUP BY分組的所有值進行排序。如果不需要排序可以禁掉以提高效率。
如 “GROUP BY col1,col2,....;” 查詢的方法如同在查詢中指定 “ORDER BY col1,col2,...;”
如果顯式包括一個包含相同的列的 ORDER BY子句,MySQL 可以毫不減速地對它進行優化,儘管仍然進行排序。
因此,如果查詢包括 GROUP BY 但你並不想對分組的值進行排序,你可以指定 ORDER BY NULL禁止排序。例如:
SELECT sid,score FROM sc GROUP BY sid,score ORDER BY NULL
使用JOIN
使用子查詢一般能有一個比較直觀的結構,能把一些複雜的查詢細分,但是有些情況下,換用JOIN效率更高。
因為 MySQL 不需要在記憶體中建立臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。
優化UNION
MySQL通過建立並填充臨時表的方式來執行union查詢。
除非確實要消除重複的行,否則建議使用union all。
原因在於如果沒有all這個關鍵詞,MySQL會給臨時表加上distinct選項,這會導致對整個臨時表的資料做唯一性校驗,這樣做的消耗相當高。