很多時候,我們的程式出現的“效能問題”,其實是我們自己寫的那"坨"程式碼的問題,是自己Coding的問題,是Mysql的DML語句使用的問題。 以下是我總結的關於MySQL DML語句的使用中需要注意的點。
對於select *
要時刻保持謹慎的態度
絕大多數情況,是不需要select *
的。一旦使用了這種語句,便會讓優化器無法完成索引覆蓋掃描這類優化,而且還會增加額外的I/O、記憶體和CPU的消耗。
當然,使用select *
也並不是全是壞處,合理的使用select *
可以簡化開發,提高相同程式碼的複用性。
是否掃描的太多額外的記錄
有時候會發現某些查詢可能需要讀取幾千行資料,但是僅返回幾條或者很少的結果,可以使用以下方式去優化:
- 看看能否改表結構。例如使用匯總表
- 看看獲取資料結果的方式是否最優,獲取路勁是否已經是最短。
- 使用覆蓋索引,把所有需要的列都放到索引中,以減少返回表中對應行中取資料的步驟。
切分某些SQL語句
傳統的網際網路系統中,強調網路連線儘量少,資料層儘可能在一次連線中完成儘可能多的工作,防止建立多次連結,但是這種想法對於MySQL並不適用,MySQL從設計上讓連線和斷開都很輕量,在一般伺服器上可以支援每秒超過10萬的查詢。
所以對於有些場景下,可以將一個大的查詢“分而治之”,切分成小查詢,然後再組合起來。例如以下情況:
- 對於全量資料查詢變成分頁。假如一張表中有數千萬條資料,一次select all,肯定是不行的。可以換成一次取一部分,把一次的壓力分攤。
- 刪除大量舊資料的時候,不要一個大的語句一次性清完,推薦
一次刪一萬條
。如果用一個大的語句一次性完成的話,可能需要一次鎖住大量資料,佔滿大量日誌事務,讓Mysql停在那兒了,為避免這種情況發生,最好一次性刪除一萬條左右的資料,然後每次刪完暫停一會兒再操作,將伺服器上的一次性壓力分散。
注意:雖然Mysql建立連線十分輕量,但是這不意味著可以逐條迴圈中查詢然後再拼接,這樣效率依然是非常慢,而且通常是工作中sql優化的點。
慎用join
操作
這算是一條禁忌吧,很多公司的網際網路產品都杜絕join操作,換成先從一張表中先取出資料id,再從另外一張表中使用where in
查詢的兩次單表查詢操作。主要是以下幾點原因:
- 讓應用的快取(redis、memcache等)更高效。例如在第一張表中查詢出部分id了,如果命中了快取,就可以省去一條where in語句了。
- 更容易應對業務的發展,方便對資料庫進行拆分,更容易做到高效能和高擴充套件。
- 對where in中的id進行升序排序後,查詢效率比join的隨機關聯更高效
- 減少多餘的查詢。在應用層中兩次查詢,意味著對某條記錄應用只需要查詢一次,而使用join可能需要重複的掃描訪問一部分資料。
- 單張表查詢可以減少鎖的競爭。
假如非用不可,可以採用以下方式來優化:
- 確保
ON
或者using
子句中的列上有索引 - 確保任何的
group by
和order by
中的表示式只涉及到一個表中的列。
在效能要求比較高的場景中,杜絕查詢中使用臨時表
MySQL的臨時表示沒有任何索引的,使用臨時表一般都意味著效能比較低,因此在對效能要求比較高的場景中,最好不要使用帶有臨時表的操作:
- 未帶索引的欄位上的
group by
操作。 UNION
查詢。- 查詢語句中的子查詢。
- 部分
order by
操作,例如distinct
函式和order by
一起使用且distinct
和order by
同一個欄位。再例如某些情況下group by
和order by
欄位不同。
具體是否用到臨時表,可以通過explain
來檢視,檢視Extra
列的結果,如果出現Using temporary
則需要注意。
count()
函式優化
count()
函式有一點需要特別注意:它是不統計值為NULL的欄位的!所以:不能指定查詢結果的某一列,來統計結果行數。即count(xx column)
不太好。
如果想要統計結果集,就使用count(*)
,效能也會很好。
儘量不使用子查詢
儘量別使用子查詢,儘可能的使用關聯來代替
優化分頁limit
通常我們在分頁的時候,通常使用的是limit 50, 10
這種語句。資料少還不錯,但是當資料偏移量非常大的時候,效能就會出現問題,例如select xx,xxx from test_table limit 100000020, 20
。掃描了100000020條資料,才返回20條資料。這個時候我們可以用一下兩種方式來優化:
利用between and
和主鍵索引
利用主鍵自增id,我們如果知道了分頁的上邊界,以上查詢可以改寫為:
select xxx, xxx from test_table where id between xxxxx and xxxx
。
利用自增主鍵索引、order by
加limit
,不使用offset
limit
和offset
的問題,其實就是offset
的問題,它會導致MySQL掃描大量不需要的行然後再拋棄掉。如果使用某個標籤記錄上一次所取資料的位置,那麼下次就可以直接從書籤位置開始掃描,這樣就可以避免使用offset
。
例如以上查詢可以改為:
第一組資料:``select xxx, xxxx from test_table order by id desc limit 20;
這樣就拿到了本次資料和下次資料的分解id值,則下一頁查詢就知道可以:
select xxx, xxx from test_table where id < '上頁id分界值' order by id desc limit 20
熟悉並靈活使用explain
以下是mysql執行查詢的整個過程,explain
可以檢視圖中標紅部分,
explain
會展示很多欄位和內容,其中的內容往往不好記,使用的時候,可以檢視以下圖解內容:
explain圖解