查詢優化器的任務是發現執行SQL查詢的最佳方案。大多數查詢優化器,包括MySQL的查詢優化器,總或多或少地在全部可能的查詢評估方案中搜索最佳方案。對於聯接查詢,MySQL優化器所調查的可能的方案數隨查詢中所引用的表的數目呈指數增加。對於小數量的表(典型小於7-10),這不是一個問題。然而,當提交的查詢更大時,查詢優化所花的時間會很容易地成爲服務器性能的主要瓶頸。
查詢優化的一個更加靈活的方法是容許用戶控制優化器詳盡地搜索最佳查詢評估方案。通常思想是優化器調查的方案越少,它編譯一個查詢所花費的時間越少。另外一方面,由於優化器跳過了一些方案,它可能錯過一個最佳方案。
優化器關於方案數量評估的行爲能夠經過兩個系統變量來控制:html
optimizer_prune_level變量告訴優化器根據對每一個表訪問的行數的估計跳過某些方案。咱們的試驗顯示該類「有根據的猜想」不多錯過最佳方案,而且能夠大大下降查詢編輯次數。這就是爲何默認狀況該選項爲on(optimizer_prune_level=1)。然而,若是你認爲優化器錯過了一個更好的查詢方案,則該選項能夠關閉(optimizer_prune_level=0),風險是查詢編輯花費的時間更長。請注意即便使用該啓發,優化器仍然能夠探測呈指數數目的方案。mysql
ptimizer_search_depth變量告訴優化器對於每一個未完成的「將來的」方案,應查看多深,以評估是否應對它進一步擴大。optimizer_search_depth值較小會使查詢編輯次數大大減少。例如,若是optimizer_search_depth接近於查詢中表的數量,對十二、13或更多表的查詢極可能須要幾小時甚至幾天的時間來編譯。同時,若是用optimizer_search_depth等於3或4編輯,對於同一個查詢,編譯器編譯時間能夠少於1分鐘。若是不能肯定合理的optimizer_search_depth值,該變量能夠設置爲0,告訴優化器自動肯定該值。
咱們能夠經過show variables 來查看這些參數
備註(手冊網址:http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter)sql
從官方手冊上看,能夠理解爲,MySQL採用了基於開銷的優化器,以肯定處理查詢的最解方式,也就是說執行查詢以前,都會先選擇一條自覺得最優的方案,而後執行這個方案來獲取結果。在不少狀況下,MySQL可以計算最佳的可能查詢計劃,但在某些狀況下,MySQL沒有關於數據的足夠信息,或者是提供太多的相關數據信息,估測就不那麼友好了。
可是感受手冊上,並無說MySQL怎麼去尋找最優方案呢?
經過查詢相應的資料,我的理解以下
MySQL優化器中,一個主要的目標是隻要可能就是用索引,並且使用條件最嚴格的索引來儘量多、儘量快地排除那些不符合索引條件的數據行,說白了就是選擇怎樣使用索引,固然優化器還受其餘的影響。爲了更直觀,下面將經過例子來講明。
建立一個表:緩存
CREATE TABLE t8( id1 INT NOT NULL , id2 INT NOT NULL, KEY id1_key(`id1`), KEY id2_key(`id2`) ) ENGINE=MYISAM DEFAULT CHARSET=utf8;
插入幾行數據以下:
當我執行以下查詢語句時候,查詢優化器會怎樣進行優化呢?服務器
select * from t8 where id1=1 and id2=0;
固然,MySQL不會傻到,從t8表中的一行開始,而後一行行的去比較,id1與id2。優化器會先分析數據表,得知有索引id1_key與id2_key,若是先判斷id1_key的話,而後須要從4行數據中排除3行數據;若是先判斷id2_key的話,而後須要從2行中排除1行。對人來講,這兩種方式沒有什麼區別,可是對於程序而言,先判斷id2_key須要較少的計算和磁盤輸入輸出。所以,查詢優化器會規定程序,先去檢驗id2_key索引,而後在從中挑出id2爲0的數據行。
經過下圖,咱們能夠看出,能夠選擇的索引有id1_key與id2_key,可是實際用到的索引只有id2_key
若是將SQL語句改成 select * from t8 where id1=1 and id2=0;
執行狀況也是同樣的,不區分先後。以下圖:
mysql優化
固然,若是將程序,修改成以下併發
select * from t8 where id1=5 and id2=0;
也能夠分析得出,會使用id1_key索引
高併發
固然,若是在建立一個複合索引性能
ALTER TABLE t8 ADD KEY id1_id2_key(`id1`,`id2`)
此時,在此執行select * from t8 where id1=1 and id2=0;
固然會考慮使用id1_id2_key索引。
經過上面的例子,能夠理解查詢優化器在查詢的時候,是選擇哪個索引做爲最合適的索引。除此,也提示咱們,要慎重選擇建立索引。如,上面建立了三個索引(id1_key、id1_key、id1_id2_key),可是優化器優化程序時候,每次只能從中選擇一個最合適的,若是建立過多,不只僅是給數據的更新和插入帶來了壓力,同時也增長了優化器的壓力。優化
其實,在上面已經查看過優化器優化過程當中的信息,無非就是使用explain。在這裏,在集中說說,裏面的參數意義。以下圖
id: MySQL Query Optimizer 選定的執行計劃中查詢的序列號。表示查詢中執行 select 子句或操做表的順序,id值越大優先級越高,越先被執行。id 相同,執行順序由上至下。
select_type:查詢類型,SIMPLE、PRIMARY、UNION、DEPENDENT UNION等。
table:顯示這一行的數據是關於哪張表的
type:這是重要的列,顯示鏈接使用了何種類型。從最好到最差的鏈接類型爲const、eq_reg、ref、range、indexhe和all
possible_keys:顯示可能應用在這張表中的索引。若是爲空,沒有可能的索引。能夠爲相關的域從where語句中選擇一個合適的語句
key: 實際使用的索引。若是爲null,則沒有使用索引。不多的狀況下,mysql會選擇優化不足的索引。這種狀況下,能夠在select語句中使用use index(indexname)來強制使用一個索引或者用ignore index(indexname)來強制mysql忽略索引
key_len:使用的索引的長度。在不損失精確性的狀況下,長度越短越好
ref:顯示索引的哪一列被使用了,若是可能的話,是一個常數
rows:mysql認爲必須檢查的用來返回請求數據的行數
extra:關於mysql如何解析查詢的額外信息。
當咱們在執行select * from t8 where id1=1 and id2=0;
語句的時候,優化器會id1_id2_key索引,但咱們能夠經過IGNORE INDEX、 IGNORE INDEX來影響索引的選擇
經過FORCE INDEX(索引1[,索引2])或者使用USE INDEX(索引1[,索引2]),來指定使用哪一個索引,也能夠指定多個索引,讓優化器從中挑選。
可使用IGNORE INDEX(索引1[,索引2])來忽略一些索引,這樣優化器,就不會考慮使用這些全部,減小優化器優化時間。
通常狀況下,MySQL優化器會自行決定按照哪一種順序掃描數據表才能最快地檢索出數據,可是咱們能夠經過STRAGHT_JOIN強制優化器按特定的順序使用數據表,畢竟優化器作的判斷不必定都是最優的。使用原則是,讓限制最強的選取操做最早執行。STRAIGHT_JOIN能夠放在SELECT後面,也能夠放在FROM子句中。
以下圖
能夠看出,不管from t8,t6仍是from t6,t8,都是先檢索t6中的表。可是使用STRAIGHT_JOIN的話,就會按照SQL中順序。
爲何優化器要選擇先判斷t6中的數據呢?一個主要的緣由,由於t6中數據更少。
若是將t8中數據刪除幾行後,很明顯MySQL優化器選擇順序數據表的順序就會發生變化。
在高併發的網站中,由於MySQL默認的是寫優先,有可能致使一些讀操做有效時間內得不到執行機會,HIGH_PRIORITY可使用在select和insert操做中,讓MYSQL知道,這個操做優先進行。
LOW_PRIORITY可使用在insert和update操做中,讓mysql知道,這個操做將優先權將下降。
INSERT DELAYED告訴MySQL,這個操做將會延時插入。
INSERT DELAYED INTO,是客戶端提交數據給MySQL,MySQL返回OK狀態給客戶端。而這是並非已經將數據插入表,而是存儲在內存裏面等待排隊。當mysql有空餘時,再插入。另外一個重要的好處是,來自許多客戶端的插入被集中在一塊兒,並被編寫入一個塊。這比執行許多獨立的插入要快不少,由於它較少了I/O操做。壞處是,不能返回自動遞增的ID,以及系統崩潰時,MySQL尚未來得及插入數據的話,這些數據將會丟失。
在實際開發中,一些數據對實時性要求特別高,或者並不常用(可能幾天就執行一次或兩次),這樣就須要把緩衝關了,無論這條SQL語句是否被執行過,服務器都不會在緩衝區中查找該數據,每次都會從磁盤中讀取。由於若是實時性要求特別高,緩存中數據可能和磁盤中的就不一樣步,若是數據不常用,被緩存起來,就會佔用內存。
在my.ini中的query_cache_type,使用來控制表緩存的。這個變量有三個取值:0,1,2,分別表明了off、on、demand。
0:表示query cache 是關閉。
1:表示查詢老是先到查詢緩存中查找,即便使用了sql_no_cache仍然查詢緩存,由於sql_no_cache只是不緩存查詢結果,而不是不使用查詢結果。
2:表示只有在使用了SQL_CACHE後,才先從緩衝中查詢數據,仍然將查詢結果緩存起來。
我本地緩存是關閉的,,以下圖。
關於MySQL緩存能夠參考這裏
(http://blog.csdn.net/hsd2012/article/details/51526707)
參考網址見:http://www.javashuo.com/article/p-rrxnjvnb-bb.html
MySQL索引優化分析,參考網址見:http://www.javashuo.com/article/p-whgwffok-hv.html