SQL優化(轉)
1. 負向條件查詢不能使用索引
select * from order where status!=0 and stauts!=1
not in/not exists都不是好習慣
可以優化為in查詢:
select * from order where status in(2,3)
2. 前導模糊查詢不能使用索引
select * from order where desc like ‘%XX‘
而非前導模糊查詢則可以:
select * from order where desc like ‘XX%‘
3. 數據區分度不大的字段不宜使用索引
select * from user where sex=1
原因:性別只有男,女,每次過濾掉的數據很少,不宜使用索引。
經驗上,能過濾80%數據時就可以使用索引。對於訂單狀態,如果狀態值很少,不宜使用索引,如果狀態值很多,能夠過濾大量數據,則應該建立索引。
4. 在屬性上進行計算不能命中索引
select * from order where YEAR(date) < = ‘2017‘
即使date上建立了索引,也會全表掃描,可優化為值計算:
select * from order where date < = CURDATE()
或者:
select * from order where date < = ‘2017-01-01‘
5. 如果業務大部分是單條查詢,使用Hash索引性能更好,例如用戶中心
select * from user where uid=?
select * from user where login_name=?
原因:
B-Tree索引的時間復雜度是O(log(n))
Hash索引的時間復雜度是O(1)
6. 允許為null的列,查詢有潛在大坑
列索引不存null值,復合索引不存全為null的值,如果列允許為null,可能會得到“不符合預期”的結果集
select * from user where name != ‘shenjian‘
如果name允許為null,索引不存儲null值,結果集中不會包含這些記錄。
所以,請使用not null約束以及默認值。
7. 復合索引最左前綴,並不是值SQL語句的where順序要和復合索引一致
用戶中心建立了(login_name, passwd)的復合索引
select * from user where login_name=? and passwd=?
select * from user where passwd=? and login_name=?
都能夠命中索引
select * from user where login_name=?
也能命中索引,滿足復合索引最左前綴
select * from user where passwd=?
不能命中索引,不滿足復合索引最左前綴
8. 如果明確知道只有一條結果返回,limit 1能夠提高效率
select * from user where login_name=?
可以優化為:
select * from user where login_name=? limit 1
原因:
你知道只有一條結果,但數據庫並不知道,明確告訴它,讓它主動停止遊標移動
9. 把計算放到業務層而不是數據庫層,除了節省數據的CPU,還有意想不到的查詢緩存優化效果
select * from order where date < = CURDATE()
這不是一個好的SQL實踐,應該優化為:
$curDate = date(‘Y-m-d‘);
$res = mysql_query(‘select * from order where date < = $curDate‘);
原因:
釋放了數據庫的CPU
多次調用,傳入的SQL相同,才可以利用查詢緩存
10. 強制類型轉換會全表掃描
select * from user where phone=13800001234
優化:
phone字段為字符串類型,給phone值加上引號,變為:‘13800001234’
11. MySQL的or/in/union與索引優化
1).union all 肯定是能夠命中索引的
2).簡單的in能夠命中索引
3).對於or,新版的MySQL能夠命中索引
4).對於!=,負向查詢肯定不能命中索引
12. 禁止使用SELECT *,只獲取必要的字段,需要顯示說明列屬性
解讀:
1).讀取不需要的列會增加CPU、IO、NET消耗
2).不能有效的利用覆蓋索引
3).使用SELECT *容易在增加或者刪除字段後出現程序BUG
13. 禁止使用INSERT INTO t_xxx VALUES(xxx),必須顯示指定插入的列屬性
解讀:容易在增加或者刪除字段後出現程序BUG
14. 禁止使用屬性隱式轉換
解讀:SELECT uid FROM t_user WHERE phone=13812345678 會導致全表掃描,而不能命中phone索引
15. 禁止在WHERE條件的屬性上使用函數或者表達式
解讀:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-02-15‘ 會導致全表掃描
正確的寫法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00‘)
16. 禁止負向查詢,以及%開頭的模糊查詢
解讀:
1).負向查詢條件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,會導致全表掃描
2).%開頭的模糊查詢,會導致全表掃描
17. 禁止大表使用JOIN查詢,禁止大表使用子查詢
解讀:會產生臨時表,消耗較多內存與CPU,極大影響數據庫性能
18. 禁止使用OR條件,必須改為IN查詢
解讀:舊版本Mysql的OR查詢是不能命中索引的,即使能命中索引,為何要讓數據庫耗費更多的CPU幫助實施查詢優化呢?
19. 應用程序必須捕獲SQL異常,並有相應處理
20. sql語句盡可能簡單: 一條sql只能在一個cpu運算;大語句拆小語句,減少鎖時間;一條大sql可以堵死整個庫
21. 簡單的事務:事務時間盡可能短
22. OR改寫為IN();OR改寫為UNION (畫外音:最新的mysql內核已經進行了相關優化)
23. limit高效分頁:limit越大,效率越低 select id from t limit 10000, 10; 應該改為 => select id from t where id > 10000 limit 10;
24. 使用union all替代union,union有去重開銷
25. 盡量不用連接join
26. 打散批量更新
27. 使用新能分析工具: explain;show slow log;
28. 在Join表的時候使用相當類型的例,並將其索引:如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中Join的字段是被建過索引的。這樣,MySQL內部會啟動為你優化Join的SQL語句的機制。
29. 永遠為每張表設置一個ID:我們應該為數據庫裏的每張表都設置一個ID做為其主鍵,而且最好的是一個INT型的(推薦使用UNSIGNED),並設置上自動增加的AUTO_INCREMENT標誌。
內容轉自 微信公眾號:架構師之路
轉自:MYSQL性能優化的最佳20+條經驗:https://coolshell.cn/articles/1846.html
SQL優化(轉)