Mysql優化概述與SELECT where子句優化

    本章說明如何優化MySQL性能並提供示例。優化涉及多個級別的配置,調整和測量性能。根據您的工作角色(開發人員,DBA或兩者的組合),您可以在單個SQL語句,整個應用程序,單個數據庫服務器或多個聯網數據庫服務器的級別進行優化。有時您可能會很主動,並提前計劃性能,而其他時候,您可能會在問題發生後對配置或代碼問題進行故障排除。優化CPU和內存使用率還可以提高可伸縮性,從而使數據庫能夠處理更多負載而不會降低速度。

1. 優化概述

    數據庫性能取決於數據庫級別的幾個因素,如表、查詢和配置設置。這些軟件結構會導致在硬件級別上執行CPU和I/O操作,您必須儘可能減少這些操作並使其儘可能高效。在研究數據庫性能時,首先學習軟件方面的高級規則和指南,並使用時鐘時間來衡量性能。當你成爲一名專家時,你會學到更多關於內部發生的事情,並開始測量諸如CPU週期和I/O操作之類的事情。
    典型的用戶旨在從其現有的軟件和硬件配置中獲得最佳的數據庫性能。高級用戶尋找機會改進MySQL軟件本身,或者開發自己的存儲引擎和硬件設備以擴展MySQL生態系統。

1.1 在數據庫級別進行優化

    使數據庫應用程序快速運行的最重要因素是它的基本設計:

  • 表格的結構是否正確?特別是,這些列是否具有正確的數據類型,並且每個表是否都具有適合於該工作類型的列?例如,執行頻繁更新的應用程序通常有許多表,但表的列數很少,而分析大量數據的應用程序通常有很少的表,表中的列數很多。
  • 是否有合適的索引來提高查詢效率?
  • 您是否爲每個表使用了適當的存儲引擎,並利用了您使用的每個存儲引擎的優勢和功能?特別是,事務性存儲引擎(如InnoDB)或非事務性存儲引擎(如MyISAM)的選擇對於性能和可伸縮性非常重要。

注意
    InnoDB是新表的默認存儲引擎。實際上,高級 InnoDB性能功能意味着 InnoDB表通常要比簡單MyISAM表好,尤其是對於繁忙的數據庫。

  • 每個表是否使用適當的行格式?此選擇還取決於用於表的存儲引擎。特別是,壓縮表使用更少的磁盤空間,因此讀寫數據所需的磁盤I/O更少。壓縮可用於具有InnoDB表的各種工作負載,也可用於只讀MyISAM表。
  • 應用程序是否使用適當的鎖定策略?例如,儘可能允許共享訪問,以便數據庫操作可以併發運行,並在適當時請求獨佔訪問,以便關鍵操作獲得最高優先級。再次,存儲引擎的選擇是重要的。InnoDB存儲引擎可以在不需要您參與的情況下處理大多數鎖定問題,從而在數據庫中實現更好的併發性,並減少代碼的實驗和調整量。
  • 所有用於緩存的內存區域大小是否正確?也就是說,大到可以容納頻繁訪問的數據,但不能大到使物理內存過載並導致分頁。要配置的主要內存區域是InnoDB buffer池、MyISAM key緩存和MySQL查詢緩存。
1.2 在硬件級別進行優化

    隨着數據庫變得越來越繁忙,任何數據庫應用程序最終都會達到硬件極限。DBA必須評估是否有可能調整應用程序或重新配置服務器以避免這些瓶頸,或者是否需要更多的硬件資源。系統瓶頸通常來自以下來源:

  • 磁盤搜索。磁盤查找數據需要花費時間。對於現代磁盤,此操作的平均時間通常小於10毫秒,因此理論上我們可以每秒執行約100次的搜索。這段時間隨着新磁盤的使用而緩慢改善,並且很難爲單個表進行優化。優化尋道時間的方法是將數據分發到多個磁盤上。
  • 磁盤讀寫。當磁盤位於正確的位置時,我們需要讀取或寫入數據。使用現代磁盤,一個磁盤至少可提供10–20MB/s的吞吐量。與查找相比,優化起來更容易,因爲您可以從多個磁盤並行讀取。
  • CPU週期。當數據位於主存儲器中時,我們必須對其進行處理以獲得結果。與內存量相比,擁有大表是最常見的限制因素。但是對於小表,速度通常不是問題。
  • 內存帶寬。當CPU需要的數據超出CPU緩存的容量時,主內存帶寬將成爲瓶頸。對於大多數系統來說,這是一個不常見的瓶頸,但是要意識到這一點。
1.3 平衡便攜性和性能

    要在可移植的MySQL程序中使用面向性能的SQL擴展,可以在/*! */註釋定界符中的語句中包裝特定於MySQL的關鍵字。其他SQL Server忽略註釋的關鍵字。

2. 優化SQL語句

    數據庫應用程序的核心邏輯是通過SQL語句執行的,無論是通過解釋程序直接發佈還是通過API在後臺提交。本節中的調整準則有助於加快各種MySQL應用程序的速度。該準則涵蓋了讀寫數據的SQL操作,一般SQL操作的幕後開銷以及在特定方案(例如數據庫監視)中使用的操作。

2.1 優化SELECT語句

    查詢,以SELECT語句的形式執行數據庫中的所有查找操作。無論是實現動態網頁的亞秒級響應時間,還是縮短時間以生成大量的隔夜報告,調整這些語句都是當務之急。
    除了SELECT語句之外,查詢的優化技術還適用於CREATE TABLE…as SELECT、INSERT in to…SELECT和DELETE語句中的WHERE子句等構造。這些語句有額外的性能考慮,因爲它們將寫操作與面向讀的查詢操作結合起來。
    優化查詢的主要考慮因素是:

  • 爲了使慢速SELECT … WHERE查詢更快,首先要檢查的是是否可以添加索引。在WHERE子句中使用的列上設置索引,以加快計算、篩選和最終檢索結果的速度。爲了避免浪費磁盤空間,請構造一小組索引,以加快應用程序中使用的許多相關查詢。
    索引對於使用join和外鍵等功能引用不同表的查詢尤其重要。您可以使用EXPLAIN語句來確定用於SELECT的索引。
  • 隔離和調整查詢中花費時間過多的任何部分,例如函數調用。根據查詢的結構,可以對結果集中的每一行調用一次函數,甚至可以對錶中的每一行調用一次函數,從而極大地提高了效率。
  • 最小化查詢中全表掃描的次數,尤其是對於大表。
  • 通過定期使用 ANALYZE TABLE 語句,使表統計信息保持最新,這樣優化器就擁有了構建高效執行計劃所需的信息。
  • 瞭解針對每個表的存儲引擎所特有的優化技術、索引技術和配置參數。InnoDB和MyISAM都有一套指導方針來支持和維持查詢的高性能。
  • 您可以使用第8.5.3節“優化InnoDB只讀事務”中的技術爲InnoDB表優化單查詢事務。
  • 避免以難以理解的方式轉換查詢,特別是如果優化器自動執行某些相同的轉換。
  • 如果使用基本準則之一不能輕鬆解決性能問題,請通過閱讀EXPLAIN計劃並調整索引,WHERE子句,連接子句等來調查特定查詢的內部細節 。(當您達到一定的專業水平時,閱讀 EXPLAIN計劃可能是每個查詢的第一步。)
  • 調整MySQL用於緩存的內存區域的大小和屬性。由於有效地使用了 InnoDB buffer池, MyISAM鍵高速緩存和MySQL查詢高速緩存,重複查詢的運行速度更快,因爲第二次及以後都從內存中檢索了結果。
  • 即使對於使用緩存區域快速運行的查詢,您也可能會進一步優化,以使它們需要更少的緩存,從而使您的應用程序更具可伸縮性。可伸縮性意味着您的應用程序可以處理更多的同時用戶,更大的請求等,而不會導致性能大幅下降。
  • 處理鎖定問題,其中其他會話同時訪問表可能會影響查詢速度。
2.1.1 WHERE子句優化

    本節討論可用於處理WHERE子句的優化。這些示例使用SELECT語句,但相同的優化也適用於DELETE和UPDATE語句中的WHERE子句。
    注意:由於有關MySQL優化器的工作正在進行中,因此此處未記錄MySQL執行的所有優化。
    您可能會試圖重寫查詢以使算術運算更快,同時犧牲可讀性。因爲MySQL會自動執行類似的優化,所以您通常可以避免這項工作,並將查詢保留在一個更易於理解和維護的表單中。MySQL執行的一些優化如下:

  • 刪除不必要的括號
((a AND b) AND c OR (((a AND b) AND (c AND d))))
-> (a AND b AND c) OR (a AND b AND c AND d)
  • 恆定摺疊
 (a<b AND b=c) AND a=5
-> b>5 AND b=c AND a=5
  • 恆定條件消除
(b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
-> b=5 OR b=6
  • 索引使用的常量表達式只計算一次。
  • 不帶WHERE的單個表上的COUNT(*)直接從MyISAM和MEMORY表的表信息中檢索。當只與一個表一起使用時,這也適用於任何非空表達式。
  • 提前檢測無效的常量表達式。MySQL快速檢測到某些SELECT語句是不可能的,並且不返回任何行。
  • 如果不使用GROUP BY或聚合函數(COUNT()、MIN(),等等),HAVING將與WHERE合併。
  • 對於聯接中的每個表,構造一個更簡單的WHERE,以獲得表的快速WHERE求值,並儘快跳過行。
  • 在查詢中的所有其他表之前,首先讀取所有常量表。常量表可以是以下任意一個:
    • 空表或具有一行的表。
    • 與主鍵或UNIQUE索引上的WHERE子句一起使用的表,其中所有索引部分都與常量表達式進行比較,並定義爲NOT NULL。
      以下所有表均用作常量表:
SELECT * FROM t WHERE primary_key=1;
SELECT * FROM t1,t2
  WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
  • 通過嘗試所有可能的方法,可以找到連接表的最佳連接組合。如果ORDER BY和GROUP BY子句中的所有列都來自同一個表,則在聯接時首選該表。
  • 如果存在一個ORDER BY子句和另一個GROUP BY子句,或者如果 ORDER BY或GROUP BY 包含連接隊列中第一個表以外的表中的列,則會創建一個臨時表。
  • 如果使用SQL_SMALL_RESULT修飾符,則MySQL使用內存中的臨時表。
  • 查詢每個表索引,並使用最佳索引,除非優化器認爲使用表掃描更有效。使用一次掃描是基於最佳索引是否包括了表的30%以上,但固定百分比不再決定使用索引還是掃描。優化器現在更復雜了,它的估計基於其他因素,如表大小、行數和I/O塊大小。
  • 在某些情況下,MySQL甚至可以在不查詢數據文件的情況下從索引中讀取行。如果索引中使用的所有列都是數字列,則僅索引樹用於解析查詢。
  • 在輸出每一行之前,HAVING將跳過不匹配該子句的那些行。

    快速查詢的一些示例:

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name
  WHERE key_part1=constant;

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

    MySQL只使用索引樹解析以下查詢,假設索引列是數字的:

SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;

SELECT COUNT(*) FROM tbl_name
  WHERE key_part1=val1 AND key_part2=val2;

SELECT key_part2 FROM tbl_name GROUP BY key_part1;

    以下查詢使用索引來按排序順序檢索行,而無需單獨的排序遍歷:

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... ;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... ;

    今天就先到這,我翻譯了一篇我自己看不懂的文章?
在這裏插入圖片描述

參考文檔

https://dev.mysql.com/doc/refman/5.7/en/where-optimization.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章