過(guò)程概覽
- 客戶端發(fā)送一條查詢給服務(wù)器贝奇;
- 服務(wù)器檢查查詢緩存,如果命中了緩存靠胜,則立即趕回存儲(chǔ)在緩存中的結(jié)果掉瞳。否則進(jìn)入下一階段;
- 服務(wù)器端進(jìn)行SQL解析浪漠,預(yù)處理陕习,再由優(yōu)化器生成對(duì)應(yīng)的執(zhí)行計(jì)劃;
- MySQL根據(jù)優(yōu)化器生成的執(zhí)行計(jì)劃址愿,調(diào)用存儲(chǔ)引擎的API來(lái)執(zhí)行查詢该镣;
- 將結(jié)果返回給客戶端
查詢緩存
在解析一個(gè)查詢語(yǔ)句之前,如果查詢緩存是打開(kāi)的响谓,那么MySQL會(huì)優(yōu)先檢查這個(gè)查詢是否命中查詢緩存中的數(shù)據(jù)损合。這個(gè)檢查是通過(guò)一個(gè)對(duì)大小寫敏感的哈希查找實(shí)現(xiàn)的。查詢和緩存中的查詢即使只有一個(gè)字節(jié)不同娘纷,那也不會(huì)匹配緩存結(jié)果嫁审,這種情況查詢會(huì)進(jìn)入下一個(gè)階段的處理。
如果當(dāng)前的查詢恰好命中了查詢緩存赖晶,那么在返回查詢結(jié)果之前MySQL會(huì)檢查一次用戶權(quán)限律适。這仍然是無(wú)須解析查詢SQL語(yǔ)句的,因?yàn)樵诓樵兙彺嬷幸呀?jīng)存放了當(dāng)前查詢需要訪問(wèn)的表信息遏插。如果權(quán)限沒(méi)有問(wèn)題捂贿,MySQL會(huì)跳過(guò)所有其他階段,直接從緩存中拿到結(jié)果并返回給客戶端胳嘲。這種情況下眷蜓,查詢不會(huì)被解析,不用生成執(zhí)行計(jì)劃胎围,不會(huì)被執(zhí)行吁系。
緩存配置參數(shù):
query_cache_limit: MySQL能夠緩存的最大結(jié)果,如果超出,則增加qcache_not_cached的值,并刪除查詢結(jié)果
query_cache_min_res_unit: 分配內(nèi)存塊時(shí)的最小單位大小
query_cache_size: 緩存使用的總內(nèi)存空間大小,單位是字節(jié),這個(gè)值必須是1024的整數(shù)倍,否則MySQL實(shí)際分配可能跟這個(gè)數(shù)值不同(感覺(jué)這個(gè)應(yīng)該跟文件系統(tǒng)的blcok大小有關(guān))
query_cache_type: 是否打開(kāi)緩存 OFF: 關(guān)閉德召,ON: 總是打開(kāi)
query_cache_wlock_invalidate: 如果某個(gè)數(shù)據(jù)表被鎖住,是否仍然從緩存中返回?cái)?shù)據(jù),默認(rèn)是OFF,表示仍然可以返回。
查詢管理器
在緩存中沒(méi)有命中到汽纤,則進(jìn)入MySQL語(yǔ)句的查詢管理器中進(jìn)行處理上岗。
在這里查詢語(yǔ)句可以轉(zhuǎn)換成一個(gè)快速執(zhí)行的代碼,代碼執(zhí)行的結(jié)果被送到客戶端管理器蕴坪。這個(gè)多步驟操作過(guò)程如下
- 查詢首先被解析并判斷是否合法
- 然后被重寫肴掷,去除了無(wú)用的操作并且加入預(yù)優(yōu)化部分
- 然后被重寫,去除了無(wú)用的操作并且加入預(yù)優(yōu)化部分
- 接著被優(yōu)化以便提升性能背传,并被轉(zhuǎn)換為可執(zhí)行代碼和數(shù)據(jù)訪問(wèn)計(jì)劃呆瞻。
- 然后計(jì)劃被編譯
- 最后,被執(zhí)行
這個(gè)過(guò)程涉及到如下幾個(gè)組成器件:
查詢解析器(Query parser):用于檢查查詢是否合法
查詢重寫器(Query rewriter):用于預(yù)優(yōu)化查詢
查詢優(yōu)化器(Query optimizer):用于優(yōu)化查詢
查詢執(zhí)行器(Query executor):用于編譯和執(zhí)行查詢
語(yǔ)法解析器
首先径玖,MySQL通過(guò)關(guān)鍵字將SQL語(yǔ)句進(jìn)行解析痴脾,并生成一棵對(duì)應(yīng)的“解析樹(shù)”。MySQL解析器將使用MySQL語(yǔ)法規(guī)則驗(yàn)證和解析查詢梳星。例如赞赖,它將驗(yàn)證是否使用錯(cuò)誤的關(guān)鍵字,或者使用關(guān)鍵字的順序是否正確等冤灾,再或者它還會(huì)驗(yàn)證引號(hào)是否能前后正確的匹配前域。如果查詢有錯(cuò),解析器將拒絕該查詢韵吨。比如匿垄,如果你寫成”SLECT …” 而不是 “SELECT …”,那就沒(méi)有下文了归粉。
解析器還會(huì)檢查關(guān)鍵字是否使用正確的順序椿疗,比如 WHERE 寫在 SELECT 之前會(huì)被拒絕。
然后盏浇,解析器要分析查詢中的表和字段,使用數(shù)據(jù)庫(kù)元數(shù)據(jù)來(lái)檢查:
- 表是否存在
- 表的字段是否存在
- 對(duì)某類型字段的 運(yùn)算 是否 可能(比如芽狗,你不能將整數(shù)和字符串進(jìn)行比較绢掰,你不能對(duì)一個(gè)整數(shù)使用 substring() 函數(shù))
接著,解析器檢查在查詢中你是否有權(quán)限來(lái)讀韧妗(或?qū)懭耄┍淼尉ⅰ?qiáng)調(diào)一下:這些權(quán)限由DBA分配。
查詢重寫器
在這一步顾复,我們已經(jīng)有了查詢的內(nèi)部表示班挖,重寫器的目標(biāo)是:
- 預(yù)優(yōu)化查詢
- 避免不必要的運(yùn)算
- 幫助優(yōu)化器找到合理的最佳解決方案
重寫器按照一系列已知的規(guī)則對(duì)查詢執(zhí)行檢測(cè)。如果查詢匹配一種模式的規(guī)則芯砸,查詢就會(huì)按照這條規(guī)則來(lái)重寫萧芙。下面是(可選)規(guī)則的非詳盡的列表:
- 視圖合并:如果你在查詢中使用視圖给梅,視圖就會(huì)轉(zhuǎn)換為它的 SQL 代碼。
- 子查詢扁平化:子查詢是很難優(yōu)化的双揪,因此重寫器會(huì)嘗試移除子查詢
- 去除不必要的運(yùn)算符:比如动羽,如果你用了 DISTINCT,而其實(shí)你有 UNIQUE 約束(這本身就防止了數(shù)據(jù)出現(xiàn)重復(fù))渔期,那么 DISTINCT 關(guān)鍵字就被去掉了运吓。
- 排除冗余的聯(lián)接:如果相同的 JOIN 條件出現(xiàn)兩次,比如隱藏在視圖中的 JOIN 條件疯趟,或者由于傳遞性產(chǎn)生的無(wú)用 JOIN拘哨,都會(huì)被消除。
- 常數(shù)計(jì)算賦值:如果你的查詢需要計(jì)算信峻,那么在重寫過(guò)程中計(jì)算會(huì)執(zhí)行一次倦青。比如 WHERE AGE > 10+2 會(huì)轉(zhuǎn)換為 WHERE AGE > 12 , TODATE(“日期字符串”) 會(huì)轉(zhuǎn)換為 datetime 格式的日期值站欺。
- (高級(jí))分區(qū)裁剪(Partition Pruning):如果你用了分區(qū)表姨夹,重寫器能夠找到需要使用的分區(qū)。
- (高級(jí))物化視圖重寫(Materialized view rewrite):如果你有個(gè)物化視圖匹配查詢謂詞的一個(gè)子集矾策,重寫器將檢查視圖是否最新并修改查詢磷账,令查詢使用物化視圖而不是原始表。
- (高級(jí))自定義規(guī)則:如果你有自定義規(guī)則來(lái)修改查詢(就像 Oracle policy)贾虽,重寫器就會(huì)執(zhí)行這些規(guī)則逃糟。
- (高級(jí))OLAP轉(zhuǎn)換:分析/加窗 函數(shù),星形聯(lián)接蓬豁,ROLLUP 函數(shù)……都會(huì)發(fā)生轉(zhuǎn)換(但我不確定這是由重寫器還是優(yōu)化器來(lái)完成绰咽,因?yàn)閮蓚€(gè)進(jìn)程聯(lián)系很緊,必須看是什么數(shù)據(jù)庫(kù))地粪。
重寫后的查詢接著送到優(yōu)化器取募。
查詢優(yōu)化器
現(xiàn)在語(yǔ)法樹(shù)被認(rèn)為合法的了,并且由優(yōu)化器將其轉(zhuǎn)化為執(zhí)行計(jì)劃蟆技。一條查詢可以由很多種執(zhí)行方式玩敏,最后都返回相同的結(jié)果。優(yōu)化器的作用就是找到這其中最好的執(zhí)行計(jì)劃质礼。</br>
MySQL使用的是“選取-投影-聯(lián)接”策略進(jìn)行查詢旺聚。用一個(gè)例子就可以理解: select uid,name from user where gender = 1;
- 這個(gè)select 查詢先根據(jù)where 語(yǔ)句進(jìn)行選取,而不是先將表全部查詢出來(lái)以后再進(jìn)行g(shù)ender過(guò)濾
- 這個(gè)select查詢先根據(jù)uid和name進(jìn)行屬性投影眶蕉,而不是將屬性全部取出以后再進(jìn)行過(guò)濾
- 將這兩個(gè)查詢條件聯(lián)接起來(lái)生成最終查詢結(jié)果.
MySQL使用基于成本(CBO)的優(yōu)化器砰粹,它將嘗試預(yù)測(cè)一個(gè)查詢使用某種執(zhí)行計(jì)劃的成本,并選擇其中成本最小的一個(gè)造挽。最早的時(shí)候碱璃,成本的最小單位是隨機(jī)讀取一個(gè)4K數(shù)據(jù)頁(yè)的成本弄痹,后來(lái)成本計(jì)算公式變得更加復(fù)雜,并且引入了一些“因子”來(lái)估算某些操作的代價(jià)厘贼,如當(dāng)執(zhí)行一次where條件比較的成本界酒。可以通過(guò)查詢當(dāng)前會(huì)話的last_query_cost的值來(lái)得知MySQL計(jì)算的當(dāng)前查詢的成本嘴秸。
問(wèn)題
- 緩存是內(nèi)存還是硬盤(內(nèi)存中)
- select->update->select時(shí)候毁欣,update如何通知緩存(發(fā)生更改,緩存立刻失效岳掐,寫入會(huì)導(dǎo)致緩存頻繁失效凭疮,降低效率)
資料
MySQL查詢語(yǔ)句執(zhí)行的過(guò)程
MySQL性能優(yōu)化——易實(shí)現(xiàn)的MySQL優(yōu)化方案匯總
MySQL優(yōu)化原理