mysql笔记03 查询性能优化
查詢性能優(yōu)化
1. 為什么查詢速度會(huì)慢?
? ? 1). 如果把查詢看作是一個(gè)任務(wù),那么它由一系列子任務(wù)組成,每個(gè)子任務(wù)都會(huì)消耗一定的時(shí)間。如果要優(yōu)化查詢,實(shí)際上要優(yōu)化其子任務(wù),要么消除其中一些子任務(wù),要么減少子任務(wù)的執(zhí)行次數(shù),要么讓子任務(wù)運(yùn)行的更快。
? ? 2). 通常來(lái)說,查詢的生命周期大致可以按照順序來(lái)看:從客戶端,到服務(wù)器端,然后在服務(wù)器上進(jìn)行解析,生成執(zhí)行計(jì)劃,執(zhí)行,并返回結(jié)果給客戶端。其中"執(zhí)行"可以認(rèn)為是整個(gè)生命周期中最重要的階段,這其中包括
? ? ? ? ?大量為了檢索數(shù)據(jù)到存儲(chǔ)引擎的調(diào)用以及調(diào)用后的數(shù)據(jù)處理,包括排序、分組等。
? ? 3). 在完成這些任務(wù)的時(shí)候,查詢需要在不同的地方花費(fèi)時(shí)間,包括網(wǎng)絡(luò),CPU計(jì)算,生成統(tǒng)計(jì)信息和執(zhí)行計(jì)劃、鎖等待(互斥等待)等操作,尤其是向底層存儲(chǔ)引擎檢索數(shù)據(jù)的調(diào)用操作,這些調(diào)用需要在內(nèi)存中操作、CPU操作
? ? ? ? ?和內(nèi)存不足時(shí)導(dǎo)致的IO操作上消耗時(shí)間,根據(jù)上下文不同,可能會(huì)產(chǎn)生大量的上下文切換以及系統(tǒng)調(diào)用。
2. 慢查詢基礎(chǔ):優(yōu)化數(shù)據(jù)訪問
? ? 查詢性能低下最基本的原因是訪問的數(shù)據(jù)太多。某些查詢可能不可避免地需要篩選大量數(shù)據(jù),但這并不常見。大部分性能低下的查詢都可以通過減少訪問的數(shù)量的方式進(jìn)行優(yōu)化。
? ? 對(duì)于低效查詢,可以通過下面兩個(gè)步驟來(lái)分析:
? ? 1). 確認(rèn)應(yīng)用程序是否在檢索大量超過需要的數(shù)據(jù)。這通常意味著訪問了太多的行,但有時(shí)候可能是訪問了太多的列。
? ? 2). 確認(rèn)MySQL服務(wù)器層是否在分析大量超過需要的數(shù)據(jù)行。
? ? 2.1 是否向數(shù)據(jù)庫(kù)請(qǐng)求了不需要的數(shù)據(jù)
? ? ? ? ? 1). ?一些典型案例
? ? ? ? ? ? ? ? a. 查詢不需要的記錄:一個(gè)常見的錯(cuò)誤是常常會(huì)誤以為MySQL會(huì)只返回需要的數(shù)據(jù),實(shí)際上MySQL卻是返回全部結(jié)果集在進(jìn)行計(jì)算。最簡(jiǎn)單有效的解決方法是在這樣的查詢后面加上LIMIT。
? ? ? ? ? ? ? ? b. 多表關(guān)聯(lián)時(shí)返回全部列
? ? ? ? ? ? ? ? c. 總是取出全部列:每次看到SELECT * 的時(shí)候都需要用懷疑的眼光審視,是不是真的需要返回全部列?取出全部列會(huì)讓優(yōu)化器無(wú)法完成索引覆蓋掃描這類優(yōu)化,還會(huì)為服務(wù)器帶來(lái)額外的網(wǎng)絡(luò)、IO、內(nèi)存和
? ? ? ? ? ? ? ? ? ? CPU的消耗。
? ? ? ? ? ? ? ? d. 重復(fù)查詢相同的數(shù)據(jù):比較好的方案是,當(dāng)初次查詢的時(shí)候?qū)⑦@個(gè)數(shù)據(jù)緩存起來(lái),需要的時(shí)候從緩存中取出,這樣性能會(huì)更好。
? ? 2.2 MySQL是否在掃描額外的記錄:
? ? ? ? ? 1). 對(duì)于MySQL,最簡(jiǎn)單的衡量查詢開銷的三個(gè)指標(biāo)如下:
? ? ? ? ? ? ? ?a. 響應(yīng)時(shí)間:響應(yīng)時(shí)間是兩部分之和:服務(wù)時(shí)間和排隊(duì)時(shí)間。服務(wù)時(shí)間是指數(shù)據(jù)庫(kù)處理這個(gè)查詢真正花多長(zhǎng)時(shí)間。排隊(duì)時(shí)間是指服務(wù)器因?yàn)榈却承┵Y源而沒有真正執(zhí)行查詢的時(shí)間--可能是等IO操作完成,也可能
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?是等待行鎖等等。
? ? ? ? ? ? ? ?b. 掃描的行數(shù)和返回的行數(shù):分析查詢時(shí),查看該查詢掃描的行數(shù)是非常有幫助的。這在一定程度上能夠說明該查詢找到需要的數(shù)據(jù)的效率高不高。
? ? ? ? ? ? ? ?c. 掃描的行數(shù)和訪問類型:在評(píng)估查詢開銷的時(shí)候,需要考慮一下從表中找到某一行數(shù)據(jù)的成本。MySQL有好幾種訪問方式可以查詢并返回一行結(jié)果。有些方式可能需要掃描很多行才能返回一行結(jié)果,也有些訪問
? ? ? ? ? ? ? ? ? 方式可能無(wú)需掃描就能返回結(jié)果。
? ? ? ? ? ? ? ? ? 在EXPALIN語(yǔ)句中的type列反應(yīng)了訪問類型。訪問類型有很多種,從全表掃描到索引掃描、范圍掃描、唯一索引掃描、常數(shù)引用等。這里列的這些,速度是從慢到快,掃描的行數(shù)是從多到少。你不要記住這
? ? ? ? ? ? ? ? ? 些訪問類型,但需要明白掃描表、掃描索引、范圍訪問和單值訪問的概念。
? ? ? ? ? 2). 一般MySQL能使用如下三種方式應(yīng)用WHERE條件,從好到壞依次為:
? ? ? ? ? ? ? ?a. 在?索引?中使用WHERE條件來(lái)過濾不匹配的記錄。這是在存儲(chǔ)引擎層完成的。
? ? ? ? ? ? ? ?b. 使用索引覆蓋掃描(在Extra列中出現(xiàn)Using index)來(lái)返回記錄,直接從索引中過濾不需要的記錄并返回命中的結(jié)果。這是在MySQL服務(wù)器層完成的,但無(wú)需再回表查詢記錄。
? ? ? ? ? ? ? ?c. 從數(shù)據(jù)表中返回?cái)?shù)據(jù),然后過濾掉不滿足條件的記錄(在Extra列中出現(xiàn)Using Where)。這在MySQL服務(wù)器層完成,MySQL需要先從數(shù)據(jù)表讀取記錄然后過濾。
? ? ? ? ? 3). 如果發(fā)現(xiàn)查詢需要掃描大量的數(shù)據(jù)但只返回少數(shù)的行(使用聚合函數(shù)等),那么通常可以嘗試下面的技巧去優(yōu)化它們:
? ? ? ? ? ? ? ?a. 使用索引覆蓋掃描,把所有需要用的列都放到索引中,這樣存儲(chǔ)引擎無(wú)需回表獲取對(duì)應(yīng)行就可以返回結(jié)果了。
? ? ? ? ? ? ? ?b. 改變庫(kù)表結(jié)構(gòu)。例如使用單獨(dú)的匯總表。
? ? ? ? ? ? ? ?c. 重寫這個(gè)復(fù)雜的查詢,讓MySQL優(yōu)化器能夠以更優(yōu)的方式執(zhí)行這個(gè)查詢。?
3. 重構(gòu)查詢的方式:有時(shí)候,可以將查詢轉(zhuǎn)換一種寫法讓其返回一樣的結(jié)果,但性能更好。
? ? ?3.1 一個(gè)復(fù)雜查詢還是多個(gè)簡(jiǎn)單查詢
? ? ? ? ? ?a. 設(shè)計(jì)查詢的時(shí)候一個(gè)需要考慮的重要問題是,是否需要將一個(gè)復(fù)雜的查詢分成過個(gè)簡(jiǎn)單的查詢。在傳統(tǒng)實(shí)現(xiàn)中,總是強(qiáng)調(diào)需要數(shù)據(jù)庫(kù)層完成盡可能多的工作,這樣做的邏輯在于以前總是認(rèn)為網(wǎng)絡(luò)通信、
? ? ? ? ? ? ? ?查詢解析和優(yōu)化是一件代價(jià)很高的事情。但是這樣的想法對(duì)于MySQL并不適用,MySQL從設(shè)計(jì)上讓連接和斷開連接都很輕量級(jí),在返回一個(gè)小的查詢結(jié)果方面很高效。現(xiàn)代的網(wǎng)絡(luò)速度比以前要快的多,
? ? ? ? ? ? ? ?無(wú)論是帶寬還是延遲。
? ? ? ? ? ?b. MySQL內(nèi)部每秒能夠掃描內(nèi)存中上百萬(wàn)行數(shù)據(jù),相比之下,MySQL響應(yīng)數(shù)據(jù)給客戶端就慢得多了。在其他條件都相同的時(shí)候,使用盡可能少的查詢當(dāng)然是更好的。但是有時(shí)候,將一個(gè)大查詢分解成
? ? ? ? ? ? ? ?多個(gè)小查詢也是很有必要的。
? ? ?3.2 切分查詢:刪除舊數(shù)據(jù)是一個(gè)很好的例子。定期清除大量數(shù)據(jù)時(shí),如果用一個(gè)大的語(yǔ)句一次性刪除完成的話,則可能需要一次鎖住很多數(shù)據(jù)、占滿整個(gè)事務(wù)日志、耗盡系統(tǒng)資源、阻塞很多小的但很重要的查詢。
? ? ? ? ? ?同時(shí)需要注意,如果每次刪除數(shù)據(jù)后,都暫停一會(huì)再做下一次刪除,可以經(jīng)服務(wù)器壓力分散到很長(zhǎng)的時(shí)間段中。
? ? ?3.3 分解關(guān)聯(lián)查詢:
? ? ? ? ? 分解關(guān)聯(lián)查詢的方式重構(gòu)查詢有如下的優(yōu)勢(shì):
? ? ? ? ? a. 讓緩存的效率更高。許多應(yīng)用程序可以方便地使用緩存單表查詢對(duì)應(yīng)的結(jié)果集。
? ? ? ? ? b. 將查詢分解后,執(zhí)行單個(gè)查詢可以減少鎖的競(jìng)爭(zhēng)。
? ? ? ? ? c. 在應(yīng)用層做關(guān)聯(lián),可以更容易對(duì)數(shù)據(jù)庫(kù)進(jìn)行拆分,更容易做到高性能和可擴(kuò)展性。
? ? ? ? ? d. 查詢本身效率也可能會(huì)有所提升。
? ? ? ? ? e. 可以減少冗余記錄的查詢。管理查詢中可能需要重復(fù)地訪問一部分?jǐn)?shù)據(jù)。
? ? ? ? ? f. 更進(jìn)一步,這樣做相當(dāng)于在應(yīng)用中實(shí)現(xiàn)了哈希關(guān)聯(lián),而不是使用MySQL的嵌套循環(huán)關(guān)聯(lián)。某些場(chǎng)景哈希關(guān)聯(lián)的效率要高很多。
4. 查詢執(zhí)行的基礎(chǔ):
? ? 查詢執(zhí)行的過程:
? ? 1). 客戶端發(fā)送一條查詢給服務(wù)器
? ? 2). 服務(wù)器檢查查詢緩存,如果命中了緩存,則立刻返回存儲(chǔ)在緩存中的結(jié)果。否則進(jìn)入下一階段。
? ? 3). 服務(wù)器端進(jìn)行SQL解析、預(yù)處理,再由優(yōu)化器生成對(duì)應(yīng)的執(zhí)行計(jì)劃。
? ? 4). MySQL根據(jù)優(yōu)化器生成的執(zhí)行計(jì)劃,調(diào)用存儲(chǔ)引擎的API來(lái)執(zhí)行查詢。
? ? 5). 將結(jié)果返回給客戶端。?
? ? 4.1 MySQL客戶端/服務(wù)器通信協(xié)議:MySQL客戶端和服務(wù)器之間的通信協(xié)議是"半雙工"的,這意味著,在任何一個(gè)時(shí)刻,要么是由服務(wù)器向客戶端發(fā)送數(shù)據(jù),要么是由客戶端向服務(wù)器發(fā)送數(shù)據(jù),這兩個(gè)動(dòng)作不能
? ? ? ? ? 同時(shí)發(fā)生,所以我們也無(wú)法將一個(gè)消息切換成小塊獨(dú)立來(lái)發(fā)送。
? ? ? ? ? 1). MySQL通常要等待所有的數(shù)據(jù)都已經(jīng)發(fā)送給客戶端才能釋放這條查詢所占的資源,所以接收全部結(jié)果并緩存通常可以減少服務(wù)器的壓力(?),讓查詢能夠早點(diǎn)結(jié)束、早點(diǎn)釋放相應(yīng)的資源。
? ? ? ? ? 2). 查詢狀態(tài):對(duì)于一個(gè)MySQL連接,或者說一個(gè)線程,任何時(shí)刻都有一個(gè)狀態(tài),該狀態(tài)表示了MySQL當(dāng)前正在做什么。有很多種方式能查看當(dāng)前的狀態(tài),最簡(jiǎn)單的是使用SHOW FULL PROCESSLIST命令(
? ? ? ? ? ? ? ?該命令返回結(jié)果中的Command列就表示當(dāng)前的狀態(tài))。下面將這些狀態(tài)列出來(lái),并做一個(gè)簡(jiǎn)單的解釋:
? ? ? ? ? ? ? ?a. Sleep:線程正在等待客戶端發(fā)送新的請(qǐng)求。
? ? ? ? ? ? ? ?b. Query:線程正在執(zhí)行查詢或者正在將結(jié)果發(fā)送給客戶端。
? ? ? ? ? ? ? ?c. Locked:在MySQL服務(wù)器層,該線程正在等待表鎖。
? ? ? ? ? ? ? ?d. Analyzing and statistics : 線程正在收集存儲(chǔ)引擎的統(tǒng)計(jì)信息,并生成查詢的執(zhí)行計(jì)劃。
? ? ? ? ? ? ? ?e. Coping to tmp table [on disk]:線程正在執(zhí)行查詢,并且將其結(jié)果都復(fù)制到一個(gè)臨時(shí)表中,這種狀態(tài)一般要么是在做GROUP BY操作,要么是文件排序操作,或者是UNION操作。如果這個(gè)狀態(tài)后面
? ? ? ? ? ? ? ? ? ?還有"on disk"標(biāo)記,那表示MySQL正在講一個(gè)內(nèi)存臨時(shí)表放到磁盤上。
? ? ? ? ? ? ? ?f. Sorting result:線程正在對(duì)結(jié)果集進(jìn)行排序。
? ? ? ? ? ? ? ?g. Sending data:這表示多種情況:線程可能在對(duì)多個(gè)狀態(tài)之間傳輸數(shù)據(jù),或者而在生成結(jié)果集,或者在向客戶端返回?cái)?shù)據(jù)。
? ? ?4.2 查詢緩存:在解析一個(gè)查詢語(yǔ)句之前,如果查詢緩存時(shí)打開的,那么MySQL會(huì)優(yōu)先檢查這個(gè)查詢是否命中查詢緩存中的數(shù)據(jù)。這個(gè)檢查是通過一個(gè)大小寫敏感的哈希查找實(shí)現(xiàn)的。查詢和緩存中的查詢即使只有
? ? ? ? ? ?一個(gè)字節(jié)不同,那也不會(huì)匹配緩存結(jié)果,這種情況下查詢會(huì)進(jìn)入下一個(gè)階段處理。
? ? ?4.3 查詢優(yōu)化處理:這個(gè)階段包括多個(gè)子階段:解析SQL、預(yù)處理、優(yōu)化SQL查詢計(jì)劃。這個(gè)過程中任何錯(cuò)誤(例如語(yǔ)法錯(cuò)誤)都可能終止。
? ? ? ? ? ?a. 語(yǔ)法解析器和預(yù)處理:MySQL通過關(guān)鍵字將SQL語(yǔ)句進(jìn)行解析,并生成一顆對(duì)應(yīng)的"解析樹",MySQL解析器將使用MySQL語(yǔ)法規(guī)則驗(yàn)證和解析查詢。預(yù)處理則根據(jù)一些MySQL規(guī)則進(jìn)一步檢查解析樹是否合法。
? ? ? ? ? ?b. 查詢優(yōu)化器:一條查詢語(yǔ)可以有很多執(zhí)行方式,最后都返回相同的結(jié)果。優(yōu)化器的作用是找到這其中最好的執(zhí)行計(jì)劃。MySQL使用基于成本的優(yōu)化器,它將嘗試預(yù)測(cè)一個(gè)查詢使用某種執(zhí)行時(shí)的成本,并選擇其中
? ? ? ? ? ? ? ?成本最小的一個(gè)。
? ? ? ? ? ?1). 有很多種原因會(huì)導(dǎo)致MySQL優(yōu)化器選擇錯(cuò)誤的執(zhí)行計(jì)劃,如下所示:
? ? ? ? ? ? ? ? a. 統(tǒng)計(jì)信息不準(zhǔn)確。MySQL依賴存儲(chǔ)引擎提供的統(tǒng)計(jì)信息來(lái)評(píng)估成本,但是有的存儲(chǔ)引擎提供的信息時(shí)不準(zhǔn)確的,有的偏差可能非常大。例如:InnoDB因?yàn)槠銶VCC的機(jī)構(gòu),并不維護(hù)一個(gè)數(shù)據(jù)表的行數(shù)的精確信息。
? ? ? ? ? ? ? ? b. 執(zhí)行計(jì)劃中的成本估算不等同于實(shí)際執(zhí)行的成本。
? ? ? ? ? ? ? ? c. MySQL的最優(yōu)可能和你想的最優(yōu)不一樣。你可能希望執(zhí)行時(shí)間盡可能的短,但是MySQL只是基于其成本模型選擇最優(yōu)的執(zhí)行計(jì)劃,而有些時(shí)候付這并不是最快的。
? ? ? ? ? ? ? ? d. MySQL從不考慮其他并發(fā)執(zhí)行的查詢,這可能會(huì)影響到當(dāng)前的查詢速度。
? ? ? ? ? ? ? ? f. MySQL也并不是任何時(shí)候都是基于成本的優(yōu)化。
? ? ? ? ? ? ? ? g. MySQL不會(huì)考慮不受其控制的操作成本,例如執(zhí)行存儲(chǔ)過程或者用戶自定義函數(shù)的成本。
? ? ? ? ? ? ? ? h. 優(yōu)化器有時(shí)候無(wú)法去估算所有可能的執(zhí)行計(jì)劃,所以他可能錯(cuò)估實(shí)際上最優(yōu)的執(zhí)行計(jì)劃。
? ? ? ? ?2). 優(yōu)化策略可以簡(jiǎn)單的分為兩種:一種靜態(tài)優(yōu)化,一種動(dòng)態(tài)優(yōu)化。
? ? ? ? ? ? ? a. 靜態(tài)優(yōu)化可以直接對(duì)解析樹進(jìn)行分析,并完成優(yōu)化。靜態(tài)優(yōu)化在第一次完成后就一直有效,即使使用不同的參數(shù)重復(fù)執(zhí)行也不會(huì)發(fā)生變化。可以認(rèn)為這是一種"編譯時(shí)優(yōu)化"。
? ? ? ? ? ? ? b. 動(dòng)態(tài)優(yōu)化則和查詢的上下文有關(guān),也可能和很多其他因素有關(guān)。例如WHERE條件中的取值、索引中條目對(duì)應(yīng)的數(shù)據(jù)行數(shù)等。需要在每次查詢的時(shí)候重新評(píng)估,可以認(rèn)為是一種"運(yùn)行時(shí)優(yōu)化"。
? ? ? ? ? ? ? c. MySQL對(duì)查詢的靜態(tài)優(yōu)化只需要做一次,但對(duì)查詢的動(dòng)態(tài)優(yōu)化則在每次執(zhí)行時(shí)都需要重新評(píng)估。有時(shí)候甚至在查詢的執(zhí)行過程中也會(huì)重新優(yōu)化。
? ? ? ? ?3). 下面是一些MySQL能夠處理的優(yōu)化過程:
? ? ? ? ? ? ? ?a. 重新定義關(guān)聯(lián)表的順序:
? ? ? ? ? ? ? ?b. 將外連接轉(zhuǎn)換為內(nèi)連接
? ? ? ? ? ? ? ?c. 使用等價(jià)變化規(guī)則:MySQL可以使用一些等價(jià)變化來(lái)簡(jiǎn)化并規(guī)范表達(dá)式。它可以合并和減少一些比較,還可以移除一些恒成立和一些恒不成立的判斷。例如:(5=5 ADN a>5) --> a>5。
? ? ? ? ? ? ? ?d. 優(yōu)化COUNT(),MIN()和MAX():索引和列是否可為空通常可以幫助MySQL優(yōu)化這類表達(dá)式。例如:要找到某一列的最小值,只需要查詢對(duì)應(yīng)B-Tree索引最左端的記錄,MySQL可以直接獲取索引的最小行。
? ? ? ? ? ? ? ?e. 預(yù)估并轉(zhuǎn)化為常數(shù)表達(dá)式:當(dāng)MySQL檢測(cè)到一個(gè)表達(dá)式可以轉(zhuǎn)化為常數(shù)的時(shí)候,就會(huì)一直把該表達(dá)式作為常數(shù)進(jìn)行優(yōu)化處理。
? ? ? ? ? ? ? ?f. 覆蓋索引掃描
? ? ? ? ? ? ? ?g. 子查詢優(yōu)化
? ? ? ? ? ? ? ?h. 提前終止查詢:在發(fā)現(xiàn)已滿足查詢需求的時(shí)候,MySQL總是能夠立刻終止查詢。一個(gè)典型的例子是使用LIMIT。
? ? ? ? ? ? ? ?i. 等值傳播:USING(film_id)
? ? ? ? ? ? ? ?j. 列表IN()的比較:在很多數(shù)據(jù)系統(tǒng)中,IN()完全等同于多個(gè)OR條件的子句,因?yàn)檫@兩者是完全等價(jià)的。在MySQL中這點(diǎn)是不成立的,MySQL將IN()列表中的數(shù)據(jù)先進(jìn)行排序,然后通過二分查找的方式來(lái)確定
? ? ? ? ? ? ? ? ? 列表中的值是否滿足條件,這是一個(gè)O(log n)復(fù)雜度的操作,等價(jià)地轉(zhuǎn)換成OR查詢的復(fù)雜度為O(n),對(duì)于IN()列表中有大量取值的時(shí)候,MySQL的處理速度將會(huì)更快。
? ? ? ? ?4). 數(shù)據(jù)和索引的統(tǒng)計(jì)信息:因?yàn)榉?wù)器層沒有任何統(tǒng)計(jì)信息,所以MySQL查詢優(yōu)化器在生成查詢的執(zhí)行計(jì)劃時(shí),需要向存儲(chǔ)引擎獲取相應(yīng)的統(tǒng)計(jì)信息。
? ? ? ? ?5). MySQL如何執(zhí)行關(guān)聯(lián)查詢:當(dāng)前MySQL關(guān)聯(lián)執(zhí)行的策略很簡(jiǎn)單:MySQL對(duì)任何關(guān)聯(lián)都執(zhí)行嵌套循環(huán)操作,即MySQL先在一個(gè)表中循環(huán)取出單條數(shù)據(jù),然后再嵌套循環(huán)到下一個(gè)表中尋找匹配的行,依次下去,直
? ? ? ? ? ? ? ?到所有表中匹配的行為止。然后根據(jù)各個(gè)表匹配的行,返回查詢中需要的各個(gè)列。MySQL會(huì)嘗試在最后一個(gè)關(guān)聯(lián)表中找到所有匹配的行,如果最后關(guān)聯(lián)表無(wú)法找到更多的行以后,MySQL返回到上一層次關(guān)聯(lián)表,
? ? ? ? ? ? ? ?看是否能夠找到更多匹配記錄,以此類推迭代執(zhí)行。
? ? ? ? ?6). 關(guān)聯(lián)查詢優(yōu)化器:MySQL優(yōu)化器最重要的一部分就是關(guān)聯(lián)查詢優(yōu)化,它決定了多個(gè)表關(guān)聯(lián)時(shí)順序。通常多表關(guān)聯(lián)的時(shí)候,可以有多種不同的關(guān)聯(lián)順序來(lái)獲得相同的執(zhí)行結(jié)果。關(guān)聯(lián)查詢優(yōu)化器則通過評(píng)估不同的順序
? ? ? ? ? ? ? 時(shí)的成本來(lái)選擇一個(gè)代價(jià)最小的關(guān)聯(lián)順序。優(yōu)化器會(huì)將數(shù)據(jù)量少的表先進(jìn)行查詢(個(gè)人認(rèn)為第一個(gè)查詢的表越小,臨時(shí)表就越小?嵌套查詢,減少查詢次數(shù)?)。
? ? ? ? ?7). 排序優(yōu)化:無(wú)論如何排序都是一個(gè)成本很高的操作,所以從性能角度考慮,應(yīng)盡可能避免排序或者盡可能避免對(duì)大量數(shù)據(jù)進(jìn)行排序。盡量通過索引進(jìn)行排序。當(dāng)不能使用索引生成排序結(jié)果的時(shí)候,MySQL需要自己
? ? ? ? ? ? ? 進(jìn)行排序,如果數(shù)據(jù)量小則在內(nèi)存中進(jìn)行,如果數(shù)量大則需要使用磁盤,不過MySQL將這個(gè)過程統(tǒng)一稱為文件排序,即使完全是內(nèi)存排序不需要任何磁盤文件時(shí)也是如此。
? ? ? ? ? ? ? MySQL有如下兩種排序算法:
? ? ? ? ? ? ? a. 兩次傳輸排序(舊版本使用):讀取行指針和需要排序的字段,對(duì)其進(jìn)行排序,然后再根據(jù)排序結(jié)果讀取所需要的數(shù)據(jù)行。需要進(jìn)行兩次傳輸,即需要從數(shù)據(jù)表中讀取兩次數(shù)據(jù),第二次讀取數(shù)據(jù)的時(shí)候,因?yàn)槭亲x
? ? ? ? ? ? ? ? ? 取排序列進(jìn)行排序后的所有記錄。這回產(chǎn)生大量的隨機(jī)IO。
? ? ? ? ? ? ? b. 單次傳輸排序(新版本使用):先讀取查詢所需要的所有列,然后在根據(jù)給定列進(jìn)行排序,最后直接返回排序結(jié)果。效率更高,但占用內(nèi)存更大。
? ? ? ? ? ? ? 如果查詢中有LIMIT的話,LIMIT也會(huì)在排序之后應(yīng)用的,所以即使需要返回較少的數(shù)據(jù),臨時(shí)表和需要排序的數(shù)據(jù)量仍然后非常大。貌似5.6版本有所改進(jìn),會(huì)先拋棄不滿足條件的記錄,然后再進(jìn)行排序。
? ? 4.4 查詢執(zhí)行引擎:在解析和優(yōu)化階段,MySQL將生成查詢對(duì)應(yīng)的執(zhí)行計(jì)劃,MySQL的查詢執(zhí)行引擎則根據(jù)這個(gè)執(zhí)行計(jì)劃來(lái)完成整個(gè)查詢。這里執(zhí)行計(jì)劃是一個(gè)數(shù)據(jù)結(jié)構(gòu),而不是和很多其他的關(guān)系型數(shù)據(jù)庫(kù)那樣會(huì)
? ? ? ? ? 生成對(duì)應(yīng)的字節(jié)碼。?
? ? 4.5 返回結(jié)果給客戶端:MySQL將結(jié)果集返回客戶端是一個(gè)增量、逐步返回的過程。開始生成第一條結(jié)果時(shí),MySQL就開始向客戶端逐步返回結(jié)果集了。
5. MySQL查詢優(yōu)化器的局限性:
? ? 5.1 關(guān)聯(lián)子查詢:MySQL的子查詢實(shí)現(xiàn)非常糟糕(5.6版本以后有改進(jìn))。最糟糕的一類查詢是WHERE條件中包含IN()的子查詢語(yǔ)句。
? ? ? ? ? 1). 因?yàn)槭褂肐N()加子查詢,性能經(jīng)常會(huì)非常糟,所以通常建議使用EXISTS()等效的改寫查詢來(lái)獲取更好的效率。
? ? ? ? ? 2). 一般建議使用左外連接(LEFT OUTER JOIN)代替子查詢(?)。
? ? 5.2 UNION的限制:MySQL無(wú)法將限制條件從外層"下推"到內(nèi)層,這使得原本能夠限制部分返回結(jié)果的條件無(wú)法應(yīng)用到內(nèi)層查詢的優(yōu)化上。
? ? ? ? ? 例如如果希望UNION的各個(gè)子句能夠根據(jù)LIMIT只去部分結(jié)果集,或者希望能夠先排好序再合并結(jié)果集的話,就需要在UNION的各個(gè)子句中分別使用這些語(yǔ)句。
? ? ? ? ? (SELECT first_name,last_name FROM sakila.actor ORDER BY last_name) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name) LIMIT 20;
? ? ? ? ? 優(yōu)化后:
? ? ? ? ??(SELECT first_name,last_name FROM sakila.actor ORDER BY last_name LIMIT 20) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name LIMIT 20) LIMIT 20;
? ? 5.3 當(dāng)WHERE子句包含多個(gè)復(fù)雜條件的時(shí)候,MySQL能夠訪問單個(gè)表的多個(gè)索引以合并和交叉過濾的方式來(lái)定位需要查找的行。
? ? 5.4 等值查詢:某些時(shí)候,等值查詢會(huì)帶來(lái)一些意想不到額外消耗。例如:有一個(gè)非常大的IN()列表,而MySQ優(yōu)化器發(fā)現(xiàn)存在WHERE、ON或者USING的子句。
? ? 5.5 并行執(zhí)行:MySQL無(wú)法利用多核特性來(lái)并行執(zhí)行查詢(貌似5.6以后有改進(jìn))。
? ? 5.6 哈希關(guān)聯(lián):MySQL不支持哈希關(guān)聯(lián)。
? ? 5.7 松散索引掃描:MySQL并不支持松散索引掃描,也就無(wú)法按照不連續(xù)的方式掃描一個(gè)索引。通常,MySQL的索引掃描需要先定義一個(gè)起點(diǎn)和終點(diǎn),即使需要的數(shù)據(jù)只是這段索引中的很少幾個(gè),MySQL仍需掃描這段索引中
? ? ? ? ? 的每一個(gè)條目。
? ? 5.8 最大值和最小值優(yōu)化:對(duì)于MIN()和MAX()查詢,MySQL的優(yōu)化做的并不好。例如:
? ? ? ? ? SELECT MIN(actor_id) FROM sakila.actor WHERE first_name='PENELOPE'
? ? ? ? ? 因?yàn)閒irst_name上沒有索引,所以會(huì)進(jìn)行全表掃描。如果MySQL能夠進(jìn)行主鍵掃描,那么理論上,當(dāng)MySQL讀到第一個(gè)滿足條件的記錄的時(shí)候,就是我們需要的最小值,因?yàn)橹麈I是嚴(yán)格按照actor_id大小字段排序的。
? ? ? ? ? 一個(gè)曲線優(yōu)化的辦法是移除MIN(),然后使用LIMIT來(lái)將查詢重寫。
? ?5.9 在同一個(gè)表上查詢和更新:MySQL不允許對(duì)同一張表同時(shí)進(jìn)行查詢和更新。
6. 查詢優(yōu)化器的提示(hint):如果對(duì)優(yōu)化器選擇的執(zhí)行計(jì)劃不滿意,可以使用優(yōu)化器提供的幾個(gè)提示(hint)來(lái)控制最終的執(zhí)行計(jì)劃。
7. 優(yōu)化特定類型的查詢
? ? 7.1 優(yōu)化COUNT()查詢
? ? ? ? ? 1). COUNT()是一個(gè)特殊的函數(shù),有兩種非常不同的作用:它可以統(tǒng)計(jì)某個(gè)列值的數(shù)量,也可以統(tǒng)計(jì)行數(shù)。在統(tǒng)計(jì)列值的時(shí)候要求列值是非空的(不統(tǒng)計(jì)NULL)。如果COUNT()的括號(hào)中指定了列或者列的表達(dá)式,則
? ? ? ? ? ? ? ?統(tǒng)計(jì)的就是這個(gè)表達(dá)式有值的結(jié)果數(shù)。最簡(jiǎn)單的就是我們使用count(*)的時(shí)候,這種情況下通配符*并不會(huì)向我們猜想的那樣擴(kuò)展所有的行,實(shí)際上,它會(huì)忽略所有的值而直接統(tǒng)計(jì)所有的行數(shù)。
? ? ? ? ? 2). 使用近似值:有時(shí)候某些業(yè)務(wù)場(chǎng)景并不要求完全精確的COUNT值,此時(shí)可以用近似值來(lái)代替。
? ? ? ? ? 3). 更復(fù)雜的優(yōu)化:覆蓋索引,增加匯總表等。
? ? 7.2 優(yōu)化關(guān)聯(lián)查詢:
? ? ? ? ? 1). 確保ON或者USING子句中的列上有索引。在創(chuàng)建索引的時(shí)候就要考慮到關(guān)聯(lián)的順序。當(dāng)表A和表B用到列C關(guān)聯(lián)的時(shí)候,如果優(yōu)化器關(guān)聯(lián)順序是B、A,那就不需要在B表的對(duì)應(yīng)列上建立索引。沒有用到的索引只會(huì)
? ? ? ? ? ? ? ?帶來(lái)額外的負(fù)擔(dān)。一般來(lái)說,除非有其他理由,否則只需要在關(guān)聯(lián)順序中的第二個(gè)表的相應(yīng)列上創(chuàng)建索引。
? ? ? ? ? 2). 確保任何的GROUP BY 和ORDER BY中的表達(dá)式只涉及到一個(gè)表中的列。這樣MySQL才有可能使用索引來(lái)優(yōu)化這個(gè)過程。
? ? 7.3 優(yōu)化子查詢:關(guān)于優(yōu)化子查詢我們給出的最重要的優(yōu)化建議就是盡可能使用關(guān)聯(lián)查詢代替,至少當(dāng)前MySQL版本需要這樣。
? ? 7.4 優(yōu)化GROUP BY和DISTINCT:
? ? ? ? ? 1). 它們都可以使用索引來(lái)優(yōu)化,這也是最有效的方法。
? ? ? ? ? 2). 在MySQL中,當(dāng)無(wú)法使用索引的時(shí)候,GROUP BY使用兩種策略來(lái)完成:使用臨時(shí)表或文件排序來(lái)做分組。對(duì)于任何查詢語(yǔ)句,這兩種策略的性能都有可以提升的地方。可以通過使用提示SQL_BIG_RESULT和
? ? ? ? ? ? ? ?SQL_SMALL_RESULT來(lái)讓優(yōu)化器按你希望的方式運(yùn)行。
? ? ? ? ? 3). 如果需要對(duì)關(guān)聯(lián)查詢分組(GROUP BY),并且是按照查找表中的某個(gè)列進(jìn)行分組,那么通常采用查找表的標(biāo)識(shí)列分組的效率比其他列更高。
? ? ? ? ? 4). 如果沒有通過ORDER BY子句顯式地指定排序列,當(dāng)查詢使用GROUP BY 子句的時(shí)候,結(jié)果集會(huì)自動(dòng)按照分組的列進(jìn)行排序。如果不關(guān)心結(jié)果集的順序,而這中默認(rèn)排序又導(dǎo)致了需要文件排序,則可以使用
? ? ? ? ? ? ? ?ORDER BY NULL,讓MySQL文件不再進(jìn)行排序。也可以在GROUP BY子句中直接使用DESC或者ASC關(guān)鍵字,使分組的結(jié)果集按照需要的方向排序。
? ? ? ? ? 5). 優(yōu)化GROUP BY WITH ROLLUP:分組查詢的一個(gè)變種思想就是要求MySQL對(duì)返回的分組結(jié)果再做一次超級(jí)聚合。最好的辦法盡可能的將WITH ROLLUP 功能轉(zhuǎn)移到應(yīng)用程序中處理。
? ? 7.5 優(yōu)化LIMIT分頁(yè):
? ? ? ? ? 1). 使用索引
? ? ? ? ? 2). 要優(yōu)化這種查詢,要么是在頁(yè)面中限制分頁(yè)的數(shù)量,要么是優(yōu)化大偏移量的性能。
? ? ? ? ? 3). 盡肯能的使用索引覆蓋
? ? ? ? ? 4). 延遲關(guān)聯(lián)
? ? ? ? ? 5). 有時(shí)候也可以將LIMIT查詢轉(zhuǎn)換為已知位置的查詢,讓MySQL通過范圍掃描找到對(duì)應(yīng)的結(jié)果。
? ? ? ? ? 6). 其他優(yōu)化辦法還包括使用預(yù)先計(jì)算的匯總表,或者關(guān)聯(lián)一個(gè)冗余表,冗余表只包含主鍵列和需要做排序的數(shù)據(jù)列。
? ? 7.6 優(yōu)化SQL_CALC_FOUND_ROWS:分頁(yè)的時(shí)候,另一個(gè)常用的技巧是在LIMIT語(yǔ)句中加上SQL_CALC_FOUND_ROWS提示(hint),這樣就可以獲得去掉LIMIT以滿足條件的行數(shù),因此可以作為分頁(yè)的總數(shù)。
? ? ? ? ? 用業(yè)務(wù)的手段解決:下一頁(yè),獲取更多數(shù)據(jù)等。
? ? 7.7 優(yōu)化UNION查詢:
? ? ? ? ? 1). MySQL總是通過創(chuàng)建填充臨時(shí)表的方式來(lái)執(zhí)行UNION查詢。因此很多優(yōu)化策略在UNION查詢中都沒法很好地使用。經(jīng)常需要手工地將WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各個(gè)子查詢中,以
? ? ? ? ? ? ? ?便優(yōu)化器可以充分利用這些條件進(jìn)行優(yōu)化。
? ? ? ? ? 2). 除非確實(shí)需要服務(wù)器消除重復(fù)的行,否則就一定要使用UNION ALL,這一點(diǎn)很重要。如果沒有ALL關(guān)鍵字,MySQL會(huì)給臨時(shí)表加上DISTINCT選項(xiàng),這回導(dǎo)致對(duì)臨時(shí)表做唯一性檢查。這樣做的代價(jià)非常高,
? ? ? ? ? ? ? ?即使有ALL關(guān)鍵字,MySQL仍然會(huì)使用臨時(shí)表存儲(chǔ)結(jié)果。事實(shí)上,MySQL總是經(jīng)結(jié)果放入臨時(shí)表,然后再讀出,再返回給客戶端。
? ? 7.8 靜態(tài)查詢分析:Percona Toolkit中的pt-query-advisor 能夠解析查詢?nèi)罩尽⒎治霾樵兡J?#xff0c;然后再給出所有可能存在的潛在問題的查詢,并給出足夠詳細(xì)的建議。這像是給MySQL所有的查詢做一次全面的健康
? ? ? ? ? 檢查,它能檢測(cè)出很多問題。
? ? 7.9 用戶自定義變量:
8. 一般,我們要盡量避免使用SELECT_FOR_UPDATE。不光是隊(duì)列表,任何情況下都要盡量避免。
9. 需要處理一種特殊的情況:那些正在被進(jìn)程處理,而進(jìn)程本身卻由于某種原因退出的情況。這種情況處理起來(lái)很簡(jiǎn)單。你只需要定期運(yùn)行UPDATE語(yǔ)句將它都更新成原始狀態(tài)就可以了,然后執(zhí)行SHOW PROCESSLIST,
? ? 獲得當(dāng)前正在工作的線程ID,并使用一些WHERE條件避免取到那些剛開始處理的進(jìn)程。
?
?
?
1. 是否向數(shù)據(jù)庫(kù)請(qǐng)求了不需要的數(shù)據(jù)
? ??a. 查詢不需要的記錄:一個(gè)常見的錯(cuò)誤是常常會(huì)誤以為MySQL會(huì)只返回需要的數(shù)據(jù),實(shí)際上MySQL卻是返回全部結(jié)果集在進(jìn)行計(jì)算。最簡(jiǎn)單有效的解決方法是在這樣的查詢后面加上LIMIT。
? ? b. 多表關(guān)聯(lián)時(shí)返回全部列
? ? c. 總是取出全部列:每次看到SELECT * 的時(shí)候都需要用懷疑的眼光審視,是不是真的需要返回全部列?取出全部列會(huì)讓優(yōu)化器無(wú)法完成索引覆蓋掃描這類優(yōu)化,還會(huì)為服務(wù)器帶來(lái)額外的網(wǎng)絡(luò)、IO、內(nèi)存和CPU的消耗。
? ? d. 重復(fù)查詢相同的數(shù)據(jù):比較好的方案是,當(dāng)初次查詢的時(shí)候?qū)⑦@個(gè)數(shù)據(jù)緩存起來(lái),需要的時(shí)候從緩存中取出,這樣性能會(huì)更好。
2. MySQL是否在掃描額外的記錄:
? ? 1). 一般MySQL能使用如下三種方式應(yīng)用WHERE條件,從好到壞依次為:
? ? ? ? ??a. 在索引中使用WHERE條件來(lái)過濾不匹配的記錄。這是在存儲(chǔ)引擎層完成的。
? ? ? ? ? b. 使用索引覆蓋掃描(在Extra列中出現(xiàn)Using index)來(lái)返回記錄,直接從索引中過濾不需要的記錄并返回命中的結(jié)果。這是在MySQL服務(wù)器層完成的,但無(wú)需再回表查詢記錄。
? ? ? ? ? c. 從數(shù)據(jù)表中返回?cái)?shù)據(jù),然后過濾掉不滿足條件的記錄(在Extra列中出現(xiàn)Using Where)。這在MySQL服務(wù)器層完成,MySQL需要先從數(shù)據(jù)表讀取記錄然后過濾。
? ? 3). 如果發(fā)現(xiàn)查詢需要掃描大量的數(shù)據(jù)但只返回少數(shù)的行(使用聚合函數(shù)等),那么通常可以嘗試下面的技巧去優(yōu)化它們:
? ? ? ? ? a. 使用索引覆蓋掃描,把所有需要用的列都放到索引中,這樣存儲(chǔ)引擎無(wú)需回表獲取對(duì)應(yīng)行就可以返回結(jié)果了。
? ? ? ? ? b. 改變庫(kù)表結(jié)構(gòu)。例如使用單獨(dú)的匯總表。
? ? ? ? ? c. 重寫這個(gè)復(fù)雜的查詢,讓MySQL優(yōu)化器能夠以更優(yōu)的方式執(zhí)行這個(gè)查詢。?
3. 重構(gòu)查詢的方式:切分查詢 ,分解關(guān)聯(lián)查詢
4. 數(shù)據(jù)庫(kù)連接池:預(yù)編譯
5. 優(yōu)化特定類型的查詢
? ??1). 優(yōu)化COUNT()查詢
? ? ? ? ? 1). COUNT()是一個(gè)特殊的函數(shù),有兩種非常不同的作用:它可以統(tǒng)計(jì)某個(gè)列值的數(shù)量,也可以統(tǒng)計(jì)行數(shù)。在統(tǒng)計(jì)列值的時(shí)候要求列值是非空的(不統(tǒng)計(jì)NULL)。如果COUNT()的括號(hào)中指定了列或者列的表達(dá)式,則
? ? ? ? ? ? ? ?統(tǒng)計(jì)的就是這個(gè)表達(dá)式有值的結(jié)果數(shù)。最簡(jiǎn)單的就是我們使用count(*)的時(shí)候,這種情況下通配符*并不會(huì)向我們猜想的那樣擴(kuò)展所有的行,實(shí)際上,它會(huì)忽略所有的值而直接統(tǒng)計(jì)所有的行數(shù)。
? ? ? ? ? 2). 使用近似值:有時(shí)候某些業(yè)務(wù)場(chǎng)景并不要求完全精確的COUNT值,此時(shí)可以用近似值來(lái)代替。
? ? ? ? ? 3). 更復(fù)雜的優(yōu)化:覆蓋索引,增加匯總表等。
? ??2). 優(yōu)化關(guān)聯(lián)查詢(確保關(guān)聯(lián)查詢中可以使用索引):
? ? ? ??? 1). 確保ON或者USING子句中的列上有索引。在創(chuàng)建索引的時(shí)候就要考慮到關(guān)聯(lián)的順序。當(dāng)表A和表B用到列C關(guān)聯(lián)的時(shí)候,如果優(yōu)化器關(guān)聯(lián)順序是B、A,那就不需要在B表的對(duì)應(yīng)列上建立索引。沒有用到的索引只會(huì)
? ? ? ? ? ? ? ?帶來(lái)額外的負(fù)擔(dān)。一般來(lái)說,除非有其他理由,否則只需要在關(guān)聯(lián)順序中的第二個(gè)表的相應(yīng)列上創(chuàng)建索引。
? ? ? ? ??2). 確保任何的GROUP BY 和ORDER BY中的表達(dá)式只涉及到一個(gè)表中的列。這樣MySQL才有可能使用索引來(lái)優(yōu)化這個(gè)過程。
? ?? 3). 優(yōu)化子查詢:關(guān)于優(yōu)化子查詢我們給出的最重要的優(yōu)化建議就是盡可能使用關(guān)聯(lián)查詢代替,至少當(dāng)前MySQL版本需要這樣。
? ?? 4). 優(yōu)化GROUP BY和DISTINCT:
? ? ? ? ? 1). 它們都可以使用索引來(lái)優(yōu)化,這也是最有效的方法。
? ? ? ? ? 2). 在MySQL中,當(dāng)無(wú)法使用索引的時(shí)候,GROUP BY使用兩種策略來(lái)完成:使用臨時(shí)表或文件排序來(lái)做分組。對(duì)于任何查詢語(yǔ)句,這兩種策略的性能都有可以提升的地方。可以通過使用提示SQL_BIG_RESULT和
? ? ? ? ? ? ? ?SQL_SMALL_RESULT來(lái)讓優(yōu)化器按你希望的方式運(yùn)行。
? ? ? ? ? 3). 如果需要對(duì)關(guān)聯(lián)查詢分組(GROUP BY),并且是按照查找表中的某個(gè)列進(jìn)行分組,那么通常采用查找表的標(biāo)識(shí)列分組的效率比其他列更高。
? ? ? ? ??4). 如果沒有通過ORDER BY子句顯式地指定排序列,當(dāng)查詢使用GROUP BY 子句的時(shí)候,結(jié)果集會(huì)自動(dòng)按照分組的列進(jìn)行排序。如果不關(guān)心結(jié)果集的順序,而這中默認(rèn)排序又導(dǎo)致了需要文件排序,則可以使用
? ? ? ? ? ? ? ?ORDER BY NULL,讓MySQL文件不再進(jìn)行排序。也可以在GROUP BY子句中直接使用DESC或者ASC關(guān)鍵字,使分組的結(jié)果集按照需要的方向排序。
? ? ? ? ??5). 優(yōu)化GROUP BY WITH ROLLUP:分組查詢的一個(gè)變種思想就是要求MySQL對(duì)返回的分組結(jié)果再做一次超級(jí)聚合。最好的辦法盡可能的將WITH ROLLUP 功能轉(zhuǎn)移到應(yīng)用程序中處理。
? ? ?5). 優(yōu)化LIMIT分頁(yè):
? ? ? ? ??1). 使用索引
? ? ? ? ? 2). 要優(yōu)化這種查詢,要么是在頁(yè)面中限制分頁(yè)的數(shù)量,要么是優(yōu)化大偏移量的性能。
? ? ? ? ??3). 盡肯能的使用索引覆蓋
? ? ? ? ? 4). 延遲關(guān)聯(lián)
? ? ? ? ??5). 有時(shí)候也可以將LIMIT查詢轉(zhuǎn)換為已知位置的查詢,讓MySQL通過范圍掃描找到對(duì)應(yīng)的結(jié)果。
? ? ? ? ??6). 其他優(yōu)化辦法還包括使用預(yù)先計(jì)算的匯總表,或者關(guān)聯(lián)一個(gè)冗余表,冗余表只包含主鍵列和需要做排序的數(shù)據(jù)列。
? ? ?6). 優(yōu)化SQL_CALC_FOUND_ROWS:分頁(yè)的時(shí)候,另一個(gè)常用的技巧是在LIMIT語(yǔ)句中加上SQL_CALC_FOUND_ROWS提示(hint),這樣就可以獲得去掉LIMIT以滿足條件的行數(shù),因此可以作為分頁(yè)的總數(shù)。
? ? ? ? ? 用業(yè)務(wù)的手段解決:下一頁(yè),獲取更多數(shù)據(jù)等。
? ? ?7). 優(yōu)化UNION查詢:
? ? ? ? ? 1). MySQL總是通過創(chuàng)建填充臨時(shí)表的方式來(lái)執(zhí)行UNION查詢。因此很多優(yōu)化策略在UNION查詢中都沒法很好地使用。經(jīng)常需要手工地將WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各個(gè)子查詢中,以
? ? ? ? ? ? ? ?便優(yōu)化器可以充分利用這些條件進(jìn)行優(yōu)化。
? ? ? ? ??2). 除非確實(shí)需要服務(wù)器消除重復(fù)的行,否則就一定要使用UNION ALL,這一點(diǎn)很重要。如果沒有ALL關(guān)鍵字,MySQL會(huì)給臨時(shí)表加上DISTINCT選項(xiàng),這回導(dǎo)致對(duì)臨時(shí)表做唯一性檢查。這樣做的代價(jià)非常高,
? ? ? ? ? ? ? ?即使有ALL關(guān)鍵字,MySQL仍然會(huì)使用臨時(shí)表存儲(chǔ)結(jié)果。事實(shí)上,MySQL總是經(jīng)結(jié)果放入臨時(shí)表,然后再讀出,再返回給客戶端。
? ? ?8). ?靜態(tài)查詢分析:Percona Toolkit中的pt-query-advisor 能夠解析查詢?nèi)罩尽⒎治霾樵兡J?#xff0c;然后再給出所有可能存在的潛在問題的查詢,并給出足夠詳細(xì)的建議。這像是給MySQL所有的查詢做一次全面的健康
? ? ? ? ? ?檢查,它能檢測(cè)出很多問題。
5. 是否使用查詢緩存,以及怎么使用查詢緩存。多插入時(shí),禁用查詢緩存
6. 批量插入
?
轉(zhuǎn)載于:https://www.cnblogs.com/Jtianlin/p/5154456.html
總結(jié)
以上是生活随笔為你收集整理的mysql笔记03 查询性能优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode77:Combinati
- 下一篇: SQL 中 not in 查询不到数据问