MYSQl 全表掃描以及查詢性能
-- 本文章僅用於學習,記錄
一. Mysql在一些情況下全表檢索比索引查詢更快:
1.表格數據很少,使用全表檢索會比使用索引檢索更快。一般當表格總數據小於10行並且數據行的長度非常小的時候會使用全表檢索;
2.在ON或者WHERE中,沒有使用索引列作為查詢條件;
3.使用了索引列與一個常量進行比較,但是mysql發現該索引列覆蓋的數據太大,反而全表檢索更快(一般當索引列覆蓋的數據大於全表數據的30%時,會觸發mysql使用全表檢索);
4.使用了一個覆蓋了大部分數據的索引列與另外表的列進行關聯查詢,此時mysql會使用全表檢索。
二. 可能造成mysql 全表掃描的原因/查詢性能差:
01. 使用null做為判斷條件
如:select store from flowers where name = null;
建議在設計字段時盡量將字段的默認值設為0,改為select account where nickname = 0;
02. 左模糊查詢Like %XXX%
如:select store from flowers where name like ‘%XXX%’ 或者 select store from flowers where name like ‘%XXX’
建議使用select store from flowers where name like ‘XXX%’,如果必須要用到做查詢,需要評估對當前表全表掃描造成的后果;
03. 使用or做為連接條件
如:select store from flowers where id = 1 or id = 2;
建議使用union all,改為 select store from flowers where id = 1 union all select store from flowers where id = 2;
04. 使用in時(not in)
很多時候用 exists 代替 in 是一個好的選擇。
如:select store from flowers where id in (1,2,3)
如果是連續數據,可以改為select store where id between 1 and 3;當數據較少時也可以參考union用法;
或者:select store from flowers where id in (select store_id from stores where id = 3 ),可以改為select store from flowers where id exsits (select store_id from stores where id = 3)
not in 可以對應 not exists;
05.使用!=或<>時
建議使用 <,<=,=,>,>=,between等;
06.對字段有操作時也會引起權標索引
如select amount from stores where salary * 0.8 = 1000 或者 select name from stores where sustring(title,1,3) = ‘daffodils’;
應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
建議改為: ---> select id from t where num=100*2
不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
07.使用count(*)時
如select count(*) from member
建議改為:---> select count(1) from member;
08.使用參數做為查詢條件時
如果在 where 子句中使用參數,也會導致全表掃描。
因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計划的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計划,變量的值還是未知的,因而無法作為索引選擇的輸入項
select id from t where
num=@num
建議改為強制查詢使用索引:---> select id from t with(index(索引名)) where
num=@num
09.使用手動創建的臨時表
建議使用聯合(UNION / UNION ALL)來代替手動創建的臨時表
當我們可以確認不可能出現重復結果集或者不在乎重復結果集的時候盡量使用union all而不是union ,因為union 和union all 的差異主要是前者需要將兩個或者多個結果集合並后再進行唯一性過濾操作,這就會涉及到排序,增加大量的cpu運算,增加資源消耗及延遲。
10.使用子查詢
子查詢內部執行計划器是這樣執行的:先查外表再匹配內表,而不是先查表t2,當外表的數據很大時,查詢速度會非常慢。
建議使用連接(JOIN) 來代替子查詢,MYSQl 不需要在內存中創建臨時表來完成這個邏輯上需要兩個表的查詢工作。另外:如果你的應用程序有很多JOIN 查詢,你應該確認JOIN 的字段是被建立過索引的。這樣MYSQL 內部會啟動為你優化JOIN 的sql 語句的機制。而且這些被用來JOIN的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個INT 字段JOIN在一起,MYSQL 就無法使用他們的索引。對於那些STRING類型,還需要有相同的字符集才行。
11.使用函數索引
比如:select * from t where year(d)>= 2016
由於mysql 不像oracle 那樣支持函數索引,也會直接全表掃描。
應改為---> select * from t where d>= '2016-01-01';
12.分組統計使用排序
分組統計可以禁止排序,避免不必要的order by 排序
比如 select name,sum(amount) as total_score from t group by name order by total_score
應改為---> select name,sum(amount) as total_score from t group by name
應改為---> select name,sum(amount) as total_score from t group by name
13.更新 clustered 索引數據列
應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那么需要考慮是否應將該索引建為 clustered 索引。
14.數值信息的字段設計為字符型
盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
15.不合理的字段類型
盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
16.如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統表的較長時間鎖定。
17.使用游標
盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那么就應該考慮改寫。使用基於游標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。
18.大事務操作
盡量避免大事務操作,提高系統並發能力。
19.客戶端返回大數據量
盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
