MySQL查詢步驟及優(yōu)化

過程概覽

  1. 客戶端發(fā)送一條查詢給服務器;
  2. 服務器檢查查詢緩存,如果命中了緩存,則立即趕回存儲在緩存中的結(jié)果。否則進入下一階段;
  3. 服務器端進行SQL解析,預處理,再由優(yōu)化器生成對應的執(zhí)行計劃;
  4. MySQL根據(jù)優(yōu)化器生成的執(zhí)行計劃,調(diào)用存儲引擎的API來執(zhí)行查詢;
  5. 將結(jié)果返回給客戶端

查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,那么MySQL會優(yōu)先檢查這個查詢是否命中查詢緩存中的數(shù)據(jù)。這個檢查是通過一個對大小寫敏感的哈希查找實現(xiàn)的。查詢和緩存中的查詢即使只有一個字節(jié)不同,那也不會匹配緩存結(jié)果,這種情況查詢會進入下一個階段的處理。
如果當前的查詢恰好命中了查詢緩存,那么在返回查詢結(jié)果之前MySQL會檢查一次用戶權(quán)限。這仍然是無須解析查詢SQL語句的,因為在查詢緩存中已經(jīng)存放了當前查詢需要訪問的表信息。如果權(quán)限沒有問題,MySQL會跳過所有其他階段,直接從緩存中拿到結(jié)果并返回給客戶端。這種情況下,查詢不會被解析,不用生成執(zhí)行計劃,不會被執(zhí)行。
緩存配置參數(shù):

image.png

query_cache_limit: MySQL能夠緩存的最大結(jié)果,如果超出,則增加qcache_not_cached的值,并刪除查詢結(jié)果
query_cache_min_res_unit: 分配內(nèi)存塊時的最小單位大小
query_cache_size: 緩存使用的總內(nèi)存空間大小,單位是字節(jié),這個值必須是1024的整數(shù)倍,否則MySQL實際分配可能跟這個數(shù)值不同(感覺這個應該跟文件系統(tǒng)的blcok大小有關)
query_cache_type: 是否打開緩存 OFF: 關閉,ON: 總是打開
query_cache_wlock_invalidate: 如果某個數(shù)據(jù)表被鎖住,是否仍然從緩存中返回數(shù)據(jù),默認是OFF,表示仍然可以返回。

查詢管理器

在緩存中沒有命中到,則進入MySQL語句的查詢管理器中進行處理。
在這里查詢語句可以轉(zhuǎn)換成一個快速執(zhí)行的代碼,代碼執(zhí)行的結(jié)果被送到客戶端管理器。這個多步驟操作過程如下

  1. 查詢首先被解析并判斷是否合法
  2. 然后被重寫,去除了無用的操作并且加入預優(yōu)化部分
  3. 然后被重寫,去除了無用的操作并且加入預優(yōu)化部分
  4. 接著被優(yōu)化以便提升性能,并被轉(zhuǎn)換為可執(zhí)行代碼和數(shù)據(jù)訪問計劃。
  5. 然后計劃被編譯
  6. 最后,被執(zhí)行

這個過程涉及到如下幾個組成器件:
查詢解析器(Query parser):用于檢查查詢是否合法
查詢重寫器(Query rewriter):用于預優(yōu)化查詢
查詢優(yōu)化器(Query optimizer):用于優(yōu)化查詢
查詢執(zhí)行器(Query executor):用于編譯和執(zhí)行查詢

語法解析器

首先,MySQL通過關鍵字將SQL語句進行解析,并生成一棵對應的“解析樹”。MySQL解析器將使用MySQL語法規(guī)則驗證和解析查詢。例如,它將驗證是否使用錯誤的關鍵字,或者使用關鍵字的順序是否正確等,再或者它還會驗證引號是否能前后正確的匹配。如果查詢有錯,解析器將拒絕該查詢。比如,如果你寫成”SLECT …” 而不是 “SELECT …”,那就沒有下文了。

解析器還會檢查關鍵字是否使用正確的順序,比如 WHERE 寫在 SELECT 之前會被拒絕。
然后,解析器要分析查詢中的表和字段,使用數(shù)據(jù)庫元數(shù)據(jù)來檢查:

  • 表是否存在
  • 表的字段是否存在
  • 對某類型字段的 運算 是否 可能(比如,你不能將整數(shù)和字符串進行比較,你不能對一個整數(shù)使用 substring() 函數(shù))

接著,解析器檢查在查詢中你是否有權(quán)限來讀?。ɑ?qū)懭耄┍?。強調(diào)一下:這些權(quán)限由DBA分配。

查詢重寫器

在這一步,我們已經(jīng)有了查詢的內(nèi)部表示,重寫器的目標是:

  1. 預優(yōu)化查詢
  2. 避免不必要的運算
  3. 幫助優(yōu)化器找到合理的最佳解決方案

重寫器按照一系列已知的規(guī)則對查詢執(zhí)行檢測。如果查詢匹配一種模式的規(guī)則,查詢就會按照這條規(guī)則來重寫。下面是(可選)規(guī)則的非詳盡的列表:

  1. 視圖合并:如果你在查詢中使用視圖,視圖就會轉(zhuǎn)換為它的 SQL 代碼。
  2. 子查詢扁平化:子查詢是很難優(yōu)化的,因此重寫器會嘗試移除子查詢
  3. 去除不必要的運算符:比如,如果你用了 DISTINCT,而其實你有 UNIQUE 約束(這本身就防止了數(shù)據(jù)出現(xiàn)重復),那么 DISTINCT 關鍵字就被去掉了。
  4. 排除冗余的聯(lián)接:如果相同的 JOIN 條件出現(xiàn)兩次,比如隱藏在視圖中的 JOIN 條件,或者由于傳遞性產(chǎn)生的無用 JOIN,都會被消除。
  5. 常數(shù)計算賦值:如果你的查詢需要計算,那么在重寫過程中計算會執(zhí)行一次。比如 WHERE AGE > 10+2 會轉(zhuǎn)換為 WHERE AGE > 12 , TODATE(“日期字符串”) 會轉(zhuǎn)換為 datetime 格式的日期值。
  6. (高級)分區(qū)裁剪(Partition Pruning):如果你用了分區(qū)表,重寫器能夠找到需要使用的分區(qū)。
  7. (高級)物化視圖重寫(Materialized view rewrite):如果你有個物化視圖匹配查詢謂詞的一個子集,重寫器將檢查視圖是否最新并修改查詢,令查詢使用物化視圖而不是原始表。
  8. (高級)自定義規(guī)則:如果你有自定義規(guī)則來修改查詢(就像 Oracle policy),重寫器就會執(zhí)行這些規(guī)則。
  9. (高級)OLAP轉(zhuǎn)換:分析/加窗 函數(shù),星形聯(lián)接,ROLLUP 函數(shù)……都會發(fā)生轉(zhuǎn)換(但我不確定這是由重寫器還是優(yōu)化器來完成,因為兩個進程聯(lián)系很緊,必須看是什么數(shù)據(jù)庫)。
    重寫后的查詢接著送到優(yōu)化器。

查詢優(yōu)化器

現(xiàn)在語法樹被認為合法的了,并且由優(yōu)化器將其轉(zhuǎn)化為執(zhí)行計劃。一條查詢可以由很多種執(zhí)行方式,最后都返回相同的結(jié)果。優(yōu)化器的作用就是找到這其中最好的執(zhí)行計劃。</br>

MySQL使用的是“選取-投影-聯(lián)接”策略進行查詢。用一個例子就可以理解: select uid,name from user where gender = 1;

  1. 這個select 查詢先根據(jù)where 語句進行選取,而不是先將表全部查詢出來以后再進行g(shù)ender過濾
  2. 這個select查詢先根據(jù)uid和name進行屬性投影,而不是將屬性全部取出以后再進行過濾
  3. 將這兩個查詢條件聯(lián)接起來生成最終查詢結(jié)果.

MySQL使用基于成本(CBO)的優(yōu)化器,它將嘗試預測一個查詢使用某種執(zhí)行計劃的成本,并選擇其中成本最小的一個。最早的時候,成本的最小單位是隨機讀取一個4K數(shù)據(jù)頁的成本,后來成本計算公式變得更加復雜,并且引入了一些“因子”來估算某些操作的代價,如當執(zhí)行一次where條件比較的成本。可以通過查詢當前會話的last_query_cost的值來得知MySQL計算的當前查詢的成本。

問題

  1. 緩存是內(nèi)存還是硬盤(內(nèi)存中)
  2. select->update->select時候,update如何通知緩存(發(fā)生更改,緩存立刻失效,寫入會導致緩存頻繁失效,降低效率)

資料

MySQL查詢語句執(zhí)行的過程
MySQL性能優(yōu)化——易實現(xiàn)的MySQL優(yōu)化方案匯總
MySQL優(yōu)化原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容