優化SQL,是DBA常見的工做之一。如何高效、快速地優化一條語句,是每一個DBA常常要面對的一個問題。在平常的優化工做中,我發現有不少操做是在優化過程當中必不可少的步驟。然而這些步驟重複性的執行,又會耗費DBA不少精力。因而萌發了本身編寫小工具,提升優化效率的想法。python
那選擇何種語言來開發工具呢?mysql
對於一名DBA來講,掌握一門語言配合本身的工做是很是必要的。相對於shell的簡單、perl的飄逸,Python是一種嚴謹的高級語言。其具有上手快、語法簡單、擴展豐富、跨平臺等多種優勢。不少人把它稱爲一種「膠水」語言,經過大量豐富的類庫、模塊,能夠快速搭建出本身須要的工具。算法
因而乎,這個小工具就成了我學習Python的第一個做業,我把它稱之爲「MySQL語句優化輔助工具」。並且今後之後,我深深愛上了Python,並開發了不少數據庫相關的小工具,之後有機會介紹給你們。sql
下面在介紹工具使用以前,首先說明下MySQL中語句優化經常使用的手段、方法及須要注意的問題。這也是你們在平常手工優化中,須要瞭解掌握的。shell
執行計劃是語句優化的主要切入點,經過執行計劃的判讀了解語句的執行過程。在執行計劃生成方面,MySQL與Oracle明顯不一樣,它不會緩存執行計劃,每次都執行「硬解析」。查看執行計劃的方法,就是使用EXPLAIN命令。數據庫
EXPLAIN QUERY緩存
當在一個Select語句前使用關鍵字EXPLAIN時,MySQL會解釋了即將如何運行該Select語句,它顯示了表如何鏈接、鏈接的順序等信息。服務器
EXPLAIN EXTENDED QUERY併發
當使用EXTENDED關鍵字時,EXPLAIN產生附加信息,能夠用SHOW WARNINGS瀏覽。該信息顯示優化器限定SELECT語句中的表和列名,重寫而且執行優化規則後SELECT語句是什麼樣子,而且還可能包括優化過程的其它註解。在MySQL5.0及更新的版本里均可以使用,在MySQL5.1裏它有額外增長了一個過濾列(filtered)。函數
EXPLAIN PARTITIONS QUERY
顯示的是查詢要訪問的數據分片——若是有分片的話。它只能在MySQL5.1及更新的版本里使用。
EXPLAIN FORMAT=JSON (5.6新特性)
另外一個格式顯示執行計劃。能夠看到諸如表間關聯方式等信息。
下面說明一下EXPLAIN輸出的字段含義,並由此學習如何判斷一個執行計劃。
id
MySQL選定的執行計劃中查詢的序列號。若是語句裏沒有子查詢等狀況,那麼整個輸出裏就只有一個SELECT,這樣一來每一行在這個列上都會顯示一個1。若是語句中使用了子查詢、集合操做、臨時表等狀況,會給ID列帶來很大的複雜性。如上例中,WHERE部分使用了子查詢,其id=2的行表示一個關聯子查詢。
select_type
語句所使用的查詢類型。是簡單SELECT仍是複雜SELECT(若是是後者,顯示它屬於哪種複雜類型)。經常使用有如下幾種標記類型。
子查詢內層的第一個SELECT,依賴於外部查詢的結果集。
子查詢中的UNION,且爲UNION中從第二個SELECT開始的後面全部SELECT,一樣依賴於外部查詢的結果集。
子查詢中的最外層查詢,注意並非主鍵查詢。
除子查詢或UNION以外的其餘查詢。
子查詢內層查詢的第一個SELECT,結果不依賴於外部查詢結果集。
結果集沒法緩存的子查詢。
UNION語句中的第二個SELECT開始後面的全部SELECT,第一個SELECT爲PRIMARY。
UNION中的合併結果。從UNION臨時表獲取結果的SELECT。
衍生表查詢(FROM子句中的子查詢)。MySQL會遞歸執行這些子查詢,把結果放在臨時表裏。在內部,服務器就把當作一個"衍生表"那樣來引用,由於臨時表就是源自子查詢。
table
這一步所訪問的數據庫中表的名稱或者SQL語句指定的一個別名錶。這個值多是表名、表的別名或者一個爲查詢產生的臨時表的標識符,如派生表、子查詢或集合。
type
表的訪問方式。如下列出了各類不一樣類型的錶鏈接,依次是從最好的到最差的。
系統表,表只有一行記錄。這是const錶鏈接類型的一個特例。
讀常量,最多隻有一行匹配的記錄。因爲只有一行記錄,優化程序裏該行記錄的字段值能夠被看成是一個恆定值。const用於在和PRIMARY KEY或UNIQUE索引中有固定值比較的情形。
最多隻會有一條匹配結果,通常是經過主鍵或惟一鍵索引來訪問。從該表中會有一行記錄被讀取出來以和從前一個表中讀取出來的記錄作聯合。與const類型不一樣的是,這是最好的鏈接類型。它用在索引全部部分都用於作鏈接而且這個索引是一個PRIMARY KEY或UNIQUE類型。eq_ref能夠用於在進行"="作比較時檢索字段。比較的值能夠是固定值或者是表達式,表達示中可使用表裏的字段,它們在讀表以前已經準備好了。
JOIN語句中驅動表索引引用的查詢。該表中全部符合檢索值的記錄都會被取出來和從上一個表中取出來的記錄做聯合。ref用於鏈接程序使用鍵的最左前綴或者是該鍵不是PRIMARY KEY或UNIQUE索引(換句話說,就是鏈接程序沒法根據鍵值只取得一條記錄)的狀況。當根據鍵值只查詢到少數幾條匹配的記錄時,這就是一個不錯的鏈接類型。ref還能夠用於檢索字段使用"="操做符來比較的時候。
與ref的惟一區別就是在使用索引引用的查詢以外再增長一個空值的查詢。這種鏈接類型相似ref,不一樣的是MySQL會在檢索的時候額外的搜索包含NULL值的記錄。這種鏈接類型的優化是從MySQL 4.1.1開始的,它常常用於子查詢。
查詢中同時使用兩個(或更多)索引,而後對索引結果進行合併(merge),再讀取表數據。這種鏈接類型意味着使用了Index Merge優化方法。
子查詢中的返回結果字段組合是主鍵或惟一約束。
子查詢中的返回結果字段組合是一個索引(或索引組合),但不是一個主鍵或惟一索引。這種鏈接類型相似unique_subquery。它用子查詢來代替IN,不過它用於在子查詢中沒有惟一索引的狀況下。
索引範圍掃描。只有在給定範圍的記錄纔會被取出來,利用索引來取得一條記錄。
全索引掃描。鏈接類型跟ALL同樣,不一樣的是它只掃描索引樹。它一般會比ALL快點,由於索引文件一般比數據文件小。MySQL在查詢的字段知識單獨的索引的一部分的狀況下使用這種鏈接類型。
全文索引掃描。
全表掃描。
possible_keys
該字段是指MySQL在搜索表記錄時可能使用哪一個索引。若是沒有任何索引可使用,就會顯示爲null。
key
查詢優化器從possible_keys中所選擇使用的索引。key字段顯示了MySQL實際上要用的索引。當沒有任何索引被用到的時候,這個字段的值就是NULL。
key_len
被選中使用索引的索引鍵長度。key_len字段顯示了MySQL使用索引的長度。當key字段的值爲NULL時,索引的長度就是NULL。
ref
列出是經過常量,仍是某個表的某個字段來過濾的。ref字段顯示了哪些字段或者常量被用來和key配合從表中查詢記錄出來。
rows
該字段顯示了查詢優化器經過系統收集的統計信息估算出來的結果集記錄條數。
Extra
該字段顯示了查詢中MySQL的附加信息。
filtered
這個列式在MySQL5.1裏新加進去的,當使用EXPLAIN EXTENDED時纔會出現。它顯示的是針對表裏符合某個條件(WHERE子句或聯接條件)的記錄數的百分比所做的一個悲觀估算。
EXPLAIN除了能夠顯示執行計劃外,還能夠顯示SQL改寫。所謂SQL改寫,是指MySQL在對SQL語句進行優化前,會基於一些原則進行語句的改寫,以方便後面的優化器進行優化生成更優的執行計劃。該功能是經過EXPLAIN EXTENDED+SHOW WARNINGS配合使用。下面經過示例說明一下。
從上面示例中,可看到原有語句中的IN子查詢被改寫成爲表間關聯的方式。
查看統計信息也是優化語句中必不可少的一步。經過統計信息能夠快速瞭解對象的存儲特徵如何。下面說明主要的兩類統計信息——表、索引。
系統參數不少,下面介紹幾個。
sort_buffer_size
排序區大小。其大小直接影響排序使用的算法。若是系統中排序都比較大、內存充足且併發量不是很大的狀況,能夠適當增長此參數。這個參數是針對單個Thead的。
join_buffer_size
Join操做使用內存區域大小。只有當Join是ALL、index、range或index_merge時使用到Join Buffer。若是join語句較多,能夠適當增大join_buffer_size。須要注意到是,這個值針對單個Thread。每一個Thread都會本身建立獨立的Buffer,而不是整個系統共享的Buffer,不要設置過大而形成系統內存不足。
tmp_table_size
若是內存內的臨時表超過該值,MySQL自動將它轉換爲硬盤上的MyISAM表。若是執行許多高級GROUP BY查詢而且有大量內存,則能夠增長tmp_table_size的值。
read_buffer_size
讀查詢操做所能使用的緩衝區大小。這個參數是針對單個Thead的。
在MySQL中,還有一些參數是能夠用來控制優化器行爲的。
optimizer_search_depth
這個參數控制優化器在窮舉執行計劃時的限度。若是查詢長時間處於"statistics"狀態,能夠考慮調低此參數。
optimizer_prune_level
默認是打開的,這讓優化器會根據須要掃描的行數來決定是否跳過某些執行計劃。
optimizer_switch
這個變量包含了一些開啓/關閉優化器特性的標誌位。
示例 — 干預優化器行爲(ICP特性)
默認狀況下,ICP特性是開啓的。查看一下優化器行爲。
基於二級索引的過濾查詢,使用了ICP特性,從Extra中的」Using index condition」可見。若是經過優化器開關,干預優化器行爲,又會如何呢?
從Extra可見,ICP特性已經禁用。
MySQL中也內置了一些狀態,經過這些狀態變量也可反映出語句執行的一些狀況,方便定位問題。手工執行的話,能夠在執行語句的先後分別執行SHOW STATUS命令,查看狀態的變化。固然,因狀態變量不少,對比起來不太方便,後面我介紹的小工具,能夠解決這個問題。
狀態變量不少,這裏介紹幾個。
Sort_merge_passes
排序算法已經執行的合併的數量。若是這個變量值較大,應考慮增長sort_buffer_size系統變量的值。
Sort_range
在範圍內執行的排序的數量。
Sort_rows
已經排序的行數。
Sort_scan
經過掃描表完成的排序的數量。
Handler_read_first
索引中第一條被讀的次數。讀取索引頭的次數,若是這個值很高,說明全索引掃描不少。
Handler_read_key
根據鍵讀一行的請求數。若是較高,說明查詢和表的索引正確。
Handler_read_next
按照鍵順序讀下一行的請求數。若是你用範圍約束或若是執行索引掃描來查詢索引列,該值增長。
Handler_read_prev
按照鍵順序讀前一行的請求數。
Handler_read_rnd
根據固定位置讀一行的請求數。若是執行大量查詢並須要對結果進行排序該值較高。則可能使用了大量須要MySQL掃描整個表的查詢或鏈接沒有正確使用鍵。
Handler_read_rnd_next
在數據文件中讀下一行的請求數。若是正進行大量的表掃描,該值較高。一般說明表索引不正確或寫入的查詢沒有利用索引。
MySQL的Query Profiler是一個使用很是方便的Query診斷分析工具,經過該工具能夠獲取一條Query在整個執行過程當中多種資源的消耗狀況,如CPU、IO、IPC、SWAP等,以及發生的PAGE FAULTS、CONTEXT SWITCHE等,同時還能獲得該Query執行過程當中的MySQL所調用的各個函數在源文件中的位置。
開啓
mysql> select @@profiling; mysql> set profiling=1;
默認狀況下profiling的值爲0表示MySQL SQL Profiler處於OFF狀態,開啓SQL性能分析器後profiling的值爲1。
執行SQL語句
mysql> select count(*) from t1;
獲取概要信息
使用"show profile"命令獲取當前系統中保存的多個Query的profile的概要信息。
mysql> show profiles; +----------+------------+-----------------------+ | Query_ID | Duration | Query | +----------+------------+-----------------------+ | 1 | 0.00039300 | select count(*) from t1 | +----------+------------+-----------------------+
針對單個Query獲取詳細的profile信息
在獲取概要信息以後,就能夠根據概要信息的Query_ID來獲取某個Query的執行過程當中詳細的profile信息。
mysql> show profile for query 1; mysql> show profile cpu,block io for query 1;
前面談到了多種手段,對於SQL語句的調優都有所幫助。經過下面這個小工具,能夠自動調用命令將上面這些內容一次性推給DBA,大大加速優化的過程。
模塊 - MySQLDB
模塊 - sqlparse
Python版本 = 2.7.3 (2.6.x版本應該也沒問題,3.x版本沒測試)
python mysql_tuning.py -p tuning_sql.ini -s 'select xxx'
-p 指定配置文件名稱
-s 指定SQL語句
共分兩節信息,分別是[database]描述數據庫鏈接信息,[option]運行配置信息。
server_ip = 127.0.0.1 db_user = testuser db_pwd = testpwd db_name = test
sys_parm = ON //是否顯示系統參數 sql_plan = ON //是否顯示執行計劃 obj_stat = ON //是否顯示相關對象(表、索引)統計信息 ses_status = ON //是否顯示運行先後狀態信息(激活後會真實執行SQL) sql_profile = ON //是否顯示PROFILE跟蹤信息(激活後會真實執行SQL)
包含運行數據庫的地址信息及數據版本信息。
用戶執行輸入的SQL,這部分主要是爲了後續對比SQL改寫時使用。語句顯示時使用了格式化。
腳本選擇顯示了部分與SQL性能相關的參數。這部分是寫死在代碼中的,如需擴展須要修改腳本。
下面是和優化器相關的一些參數,經過調整這些參數能夠人爲干預優化器行爲。
就是調用explain extended的輸出結果。若是結果過長,可能出現顯示串行的問題(暫時未解決)。
經過這裏可判斷優化器是否對SQL進行了某種優化(例如子查詢的處理)。
在SQL語句中全部涉及到的表及其索引的統計信息都會在這裏顯示出來。
在會話級別對比了執行先後的狀態(SHOW STATUS),並將出現變化的部分顯示出來。須要注意的是,由於收集狀態數據是採用SELECT方式,會形成個別指標的偏差(例如Com_select)。
調用SHOW PROFILE獲得的詳細信息。
根據PROFILE的資源消耗狀況,顯示不一樣階段消耗對比狀況(TOP N),直觀顯示"瓶頸"所在。
源碼文件下載地址:https://pan.baidu.com/s/1slF3...
做者:韓鋒
內容來源:宜信技術學院