1. 程式人生 > >SQL優化(轉)

SQL優化(轉)

很多 最好 原因 簡單 bsp 指定 架構師 exist hash

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優化(轉)