輕鬆優化MySQL資料庫-常用sql優化建議
避免SELECT *
從資料庫裡讀出越多的資料,那麼查詢就會變得越慢。並且如果你的資料庫伺服器和WEB伺服器是兩臺獨立的伺服器的話,這還會增加網路傳輸的負載。
···
select * from person where lname='x8RJWmQX';
select id from person where lname='x8RJWmQX';
···

image.png
避免在where子句中使用!=或<>操作符
應儘量避免在 where 子句中使用!=或<>操作符,否則引擎放棄使用索引而進行全表掃描。
EXPLAIN select * from person where fname != 'sss’ ;

image.png
儘量避免全表掃描
對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
用UNION來代替OR
採用 OR 語句:
select * from person where fname ='LVc1oJjd' or fname='bjRdlVo';
採用 UNION 語句,返回的結果同上面的一樣,但是速度要快些:
select * from person where fname ='LVc1oJjd' Union select * from person where fname='bjRdlVo';
分別對這兩個sql進行explain分析:
OR 語句的結果

image.png
UNION 語句的結果

image.png
我們來比較下重要指標,發現主要差別是 type 和 ref 這兩項。type 顯示的是訪問型別,是較為重要的一個指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
UNION 語句的 type 值為 一般為ref,OR 語句的 type 值為 range,可以看到這是一個很明顯的差距。
UNION 語句的 ref 值為 const,OR 語句的 type 值為 null,const 表示是常量值引用,非常快。
這兩項的差距就說明了 UNION 要優於 OR,從我們的直觀感覺上也可以理解,雖然這兩個方式都用到了索引,但 UNION 是用一個明確的值到索引中查詢,目標非常明確,OR 需要對比兩個值,目標相對要模糊一些,所以 OR 在恍惚中落後了。

image.png
like語句避免前置百分號
前置百分號會導致索引失效
select * from person where fname like '%LVc1o%' ;

image.png
下面走索引
select * from person where fname like 'LVc1o%' ;

image.png
避免where子句中使用引數
如果在 where 子句中使用引數,也會導致全表掃描。因為SQL只有在執行時才會解析區域性變數,但優化程式不能將訪問計劃的選擇推遲到執行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
避免在where子句中對欄位進行表示式操作
應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
避免在where子句中對欄位進行函式操作
應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)=’abc’ –name以abc開頭的id select id from t where datediff(day,createdate,’2005-11-30′)=0 –’2005-11-30′生成的id
應改為:
select id from t where name like ‘abc%’ select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
避免無意義查詢
不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類程式碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(…)
用exists代替 in
很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
儘量使用數字型欄位
儘量使用數字型欄位,若只含數值資訊的欄位儘量不要設計為字元型,這會降低查詢和連線的效能,並會增加儲存開銷。這是因為引擎在處理查詢和連線時會 逐個比較字串中每一個字元,而對於數字型而言只需要比較一次就夠了。
使用varchar/nvarchar代替char/nchar
儘可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長欄位儲存空間小,可以節省儲存空間,其次對於查詢來說,在一個相對較小的欄位內搜尋效率顯然要高些。
大臨時表使用select into代替create table
在新建臨時表時,如果一次性插入資料量很大,那麼可以使用 select into 代替 create table,避免造成大量log ,以提高速度;如果資料量不大,為了緩和系統表的資源,應先create table,然後insert。
臨時表先truncate table,然後drop table
如果使用到了臨時表,在儲存過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
儲存過程使用SET NOCOUNT ON
在所有的儲存過程和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF。無需在執行儲存過程和觸發器的每個語句後向客戶端傳送 DONEINPROC 訊息。
避免向客戶端返回大資料量
儘量避免向客戶端返回大資料量,若資料量過大,應該考慮相應需求是否合理。
避免在 where子句中對欄位進行 null 值判斷
應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num is null
可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
在Mysql5.7版本中該條建議已經不用考慮了,因為null判斷也能使用索引了。

image.png