MySQL的SQL效能優化總結

RyuGou發表於2019-02-16

很多時候,我們的程式出現的“效能問題”,其實是我們自己寫的那"坨"程式碼的問題,是自己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 byorder by中的表示式只涉及到一個表中的列。

在效能要求比較高的場景中,杜絕查詢中使用臨時表

MySQL的臨時表示沒有任何索引的,使用臨時表一般都意味著效能比較低,因此在對效能要求比較高的場景中,最好不要使用帶有臨時表的操作:

  • 未帶索引的欄位上的group by操作。
  • UNION查詢。
  • 查詢語句中的子查詢。
  • 部分order by操作,例如distinct函式和order by一起使用且distinctorder by同一個欄位。再例如某些情況下group byorder 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 bylimit,不使用offset

limitoffset的問題,其實就是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可以檢視圖中標紅部分,

MySQL的SQL效能優化總結

explain會展示很多欄位和內容,其中的內容往往不好記,使用的時候,可以檢視以下圖解內容: explain圖解

更多精彩內容,請關注我的微信公眾號 網際網路技術窩 或者加微信共同探討交流:

MySQL的SQL效能優化總結

相關文章