MySQL性能分析工具
第一節(jié) 統(tǒng)計(jì)SQL執(zhí)行成本
統(tǒng)計(jì)SQL的查詢成本: last_query_cost
一條SQL查詢語(yǔ)句在執(zhí)行前需要確定查詢執(zhí)行計(jì)劃,如果存在多種執(zhí)行計(jì)劃的話,MySQL會(huì)計(jì)算每個(gè)執(zhí)行計(jì)劃所需要的成本,從中選擇成本最小的一個(gè)作為最終執(zhí)行的執(zhí)行計(jì)劃。
如果我們想要查看某條SQL語(yǔ)句的查詢成本,可以在執(zhí)行完這條SQL語(yǔ)句之后,通過(guò)查看當(dāng)前會(huì)話中的last_query_cost變量值來(lái)得到當(dāng)前查詢的成本。它通常也是我們?cè)u(píng)價(jià)一個(gè)查詢的執(zhí)行效率的一個(gè)常用指標(biāo)。這個(gè)查詢成本對(duì)應(yīng)的是SQL語(yǔ)句所需要讀取的頁(yè)的數(shù)量。
SQL查詢是一個(gè)動(dòng)態(tài)的過(guò)程,從頁(yè)加載的角度來(lái)看,我們可以得到以下兩點(diǎn)結(jié)論:
所以說(shuō),遇到I/O并不用擔(dān)心,方法找對(duì)了,效率還是很高的。我們首先要考慮數(shù)據(jù)存放的位置,如果是經(jīng)常使用的數(shù)據(jù)就要盡量放到緩沖池中,其次我們可以充分利用磁盤(pán)的吞吐能力,一次性批量讀取數(shù)據(jù),這樣單個(gè)頁(yè)的讀取效率也就得到了提升。
第二節(jié) 分析查詢語(yǔ)句:EXPLAIN
2.1 基本語(yǔ)法
如果我們想看看某個(gè)查詢的執(zhí)行計(jì)劃的話,可以在具體的查詢語(yǔ)句前邊加一個(gè) EXPLAIN ,就像這樣:
EXPLAIN SELECT 1;EXPLAIN 語(yǔ)句輸出的各個(gè)列的作用如下:
| id | 在一個(gè)大的查詢語(yǔ)句中每個(gè)SELECT關(guān)鍵字都對(duì)應(yīng)一個(gè) 唯一的id |
| select_type | SELECT關(guān)鍵字對(duì)應(yīng)的那個(gè)查詢的類型 |
| table | 表名 |
| partitions | 匹配的分區(qū)信息 |
| type☆ | 針對(duì)單表的訪問(wèn)方法 |
| possible_keys | 可能用到的索引 |
| key | 實(shí)際上使用的索引 |
| key_len ☆ | 實(shí)際使用到的索引長(zhǎng)度 |
| ref | 當(dāng)使用索引列等值查詢時(shí),與索引列進(jìn)行等值匹配的對(duì)象信息 |
| rows ☆ | 預(yù)估的需要讀取的記錄條數(shù) |
| filtered | 某個(gè)表經(jīng)過(guò)搜索條件過(guò)濾后剩余記錄條數(shù)的百分比 |
| Extra ☆ | 一些額外的信息 |
2.2 詳細(xì)解釋
為了讓大家有比較好的體驗(yàn),下面調(diào)整了下 EXPLAIN 輸出列的順序。
2.2.1 table
不論我們的查詢語(yǔ)句有多復(fù)雜,里邊兒包含了多少個(gè)表 ,到最后也是需要對(duì)每個(gè)表進(jìn)行 單表訪問(wèn)的,所以MySQL規(guī)定EXPLAIN語(yǔ)句輸出的每條記錄都對(duì)應(yīng)著某個(gè)單表的訪問(wèn)方法,該條記錄的table列代表著該表的表名(有時(shí)不是真實(shí)的表名字,可能是簡(jiǎn)稱)。
2.2.2 id
我們寫(xiě)的查詢語(yǔ)句一般都以 SELECT 關(guān)鍵字開(kāi)頭,比較簡(jiǎn)單的查詢語(yǔ)句里只有一個(gè) SELECT 關(guān)鍵字,比如下邊這個(gè)查詢語(yǔ)句:
SELECT * FROM s1 WHERE key1 = 'a'; mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a’;
稍微復(fù)雜一點(diǎn)的連接查詢中也只有一個(gè) SELECT 關(guān)鍵字,比如:
查看mysql優(yōu)化后的sql:
小結(jié):
id如果相同,可以認(rèn)為是一組,從上往下順序執(zhí)行
在所有組中,id值越大,優(yōu)先級(jí)越高,越先執(zhí)行
關(guān)注點(diǎn):id號(hào)每個(gè)號(hào)碼,表示一次獨(dú)立的查詢, 一個(gè)sql的查詢次數(shù)越少越好
2.2.3 select_type
| SIMPLE | 簡(jiǎn)單查詢(不使用UNION或子查詢) |
| PRIMARY | 最外層的查詢 |
| UNION | UNION中第二個(gè)語(yǔ)句或后面的語(yǔ)句 |
| UNION RESULT | UNION每個(gè)結(jié)果集的取出來(lái)后,進(jìn)行合并操作 |
| dependentSubquery | 子查詢中的第一個(gè) SELECT,依賴了外面的查詢 |
| derived | 派生表,子查詢?cè)?FROM子句中 |
SIMPLE
SIMPLE,最簡(jiǎn)單的查詢方式。
primary、union、union result
PRIMARY,主查詢。
UNION,UNION中第二個(gè)語(yǔ)句或后面的語(yǔ)句。
UNION RESULT,UNION每個(gè)結(jié)果集的取出來(lái)后,進(jìn)行合并操作。
Subquery
子查詢:#如果包含子查詢的查詢語(yǔ)句不能夠轉(zhuǎn)為對(duì)應(yīng)的semi-join(多表連接)的形式,并且該子查詢是不相關(guān)子查詢。#該子查詢的第一個(gè)SELECT'關(guān)鍵字代表的那個(gè)查詢的select type '就是SUBQUERY
查詢優(yōu)化器把in換成關(guān)聯(lián)查詢
dependentSubquery
DEPENDENT SUBQUERY,子查詢中內(nèi)層的第一個(gè)SELECT,依賴于外部查詢的結(jié)果集。
derived
DERIVED,派生表,子查詢?cè)?FROM子句中。
2.2.4 partitions
EXPLAIN SELECT * FROM user_partitions WHERE id>200;查詢id大于200(200>100,p1分區(qū))的記錄,查看執(zhí)行計(jì)劃,partitions是p1,符合我們的分區(qū)規(guī)則。
2.2.5 type
完整的訪問(wèn)方法如下: system , const , eq_ref , ref , fulltext , ref_or_null ,index_merge , unique_subquery , index_subquery , range , index , ALL 。
system : 該表只有一行(相當(dāng)于系統(tǒng)表),system是const類型的特例
const:當(dāng)我們根據(jù)主鍵或者唯一二級(jí)索引列與常數(shù)進(jìn)行等值匹配時(shí),對(duì)單表的訪問(wèn)方法就是`const ’
eq_ref:#在連接查詢時(shí),如果被驅(qū)動(dòng)表是通過(guò)主鍵或者唯一二級(jí)索引列等值匹配的方式進(jìn)行訪問(wèn)的(如果該主鍵或者唯一二級(jí)索引是聯(lián)合索引的話,所有的索引列都必須進(jìn)行等值比較),則對(duì)該被驅(qū)動(dòng)表的訪問(wèn)方法就是eq _ref。
ref:當(dāng)通過(guò)普通的二級(jí)索引列與常量進(jìn)行等值匹配時(shí)來(lái)查詢某個(gè)表,那么對(duì)該表的訪問(wèn)方法就可能是`ref’,聯(lián)合索引時(shí)只匹配部分字段也為ref。
ref_or_null:當(dāng)對(duì)普通二級(jí)索引進(jìn)行等值匹配查詢,該索引列的值也可以是NULL值時(shí),那么對(duì)該表的訪問(wèn)方法就是‘ref or null`
index_merge:此類型表示使用了索引合并優(yōu)化,表示一個(gè)查詢里面用到了多個(gè)索引
unique subquery :是針對(duì)在一些包含’IN子查詢的查詢語(yǔ)句中,如果查詢優(yōu)化器決定將IN子查詢轉(zhuǎn)換為EXISTS子查詢,而且子查詢可以使用到主鍵進(jìn)行等值匹配的話,那么該子查詢執(zhí)行計(jì)劃的 type '列的值就是unique_subquery
range:表示使用了范圍類型的查詢,一般出現(xiàn)在使用了<>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 貨 IN() 之類的字句和常量值作比較時(shí)
index:當(dāng)我們可以使用索引覆蓋,但需要掃描全部的索引記錄時(shí),該表的訪問(wèn)方法就是index。
EXPLAIN SELECT coupon_id FROM coupon_give_gift where give_coupon_id = '111';ALL:全表掃描。
上述分析的過(guò)程為從上至下,效率一次降低。
2.2.6 possible_keys和key
在EXPLAIN語(yǔ)句輸出的執(zhí)行計(jì)劃中,possible_keys列表示在某個(gè)查詢語(yǔ)句中,對(duì)某個(gè)表執(zhí)行單表查詢時(shí)可能用到的索引有哪些。一般查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用。key列表示實(shí)際用到的索引有哪些,如果為NULL,則沒(méi)有使用索引。比方說(shuō)下邊這個(gè)查詢:
EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111' and promotion_id = '111’; EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111’or promotion_id = '111';2.2.7 key_len
實(shí)際使用到的索引長(zhǎng)度(即:字節(jié)數(shù))#幫你檢查是否充分的利用上了索引,同一個(gè)索引`值越大越好’,主要針對(duì)聯(lián)合索引。
| id int | key_len = 4+1 | int為4bytes,允許為NULL,加1byte(1字節(jié)用來(lái)記錄是否為null) |
| id bigint not null | key_len=8 | bigint為8bytes |
| user char(30) utf8 | key_len=30*3+1 | utf8每個(gè)字符為3bytes,允許為NULL,加1byte |
| user varchar(30) not null utf8 | key_len=30*3+2 | utf8每個(gè)字符為3bytes,變長(zhǎng)數(shù)據(jù)類型,加2bytes(2字節(jié)用來(lái)記錄實(shí)際長(zhǎng)度) |
| user varchar(30) utf8 | key_len=30*3+2+1 | utf8每個(gè)字符為3bytes,允許為NULL,加1byte,變長(zhǎng)數(shù)據(jù)類型,加2bytes |
2.2.8 ref
當(dāng)使用索引列等值查詢時(shí),與索引列進(jìn)行等值匹配的對(duì)象信息。比如只是一個(gè)常數(shù)或者是某個(gè)列。
EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111'; EXPLAIN SELECT* FROMcoupon inner JOINcoupon_give_gift on coupon_give_gift.coupon_id = coupon.coupon_id;2.2.9 rows
預(yù)估的需要讀取的記錄條數(shù),值越小越好。
EXPLAIN SELECT * FROM coupon WHERE id < 100;2.2.10 filtered
某個(gè)表經(jīng)過(guò)搜索條件過(guò)濾后剩余記錄條數(shù)的百分比。如果使用的是索引執(zhí)行的單表掃描,那么計(jì)算時(shí)需要估計(jì)出滿足除使用到對(duì)應(yīng)索引的搜索條件外的其他搜索條件的記錄有多少條。
EXPLAIN SELECT * FROM coupon WHERE id < 100 and campaign_coupon_code = '202007071120';2.2.11 Extra
顧名思義,Extra列是用來(lái)說(shuō)明一些額外信息的,包含不適合在其他列中顯示但十分重要的額外信息。我們可以通過(guò)這些額外信息來(lái)更準(zhǔn)確的理解MySQL到底將如何執(zhí)行給定的查詢語(yǔ)句。MySQL提供的額外信息有好幾十個(gè),我們就不一個(gè)一個(gè)介紹了,所以只挑比較重要的額外信息介紹給大家。
2. Impossible where 查詢語(yǔ)句的WHERE子句永遠(yuǎn)為FALSE時(shí)將會(huì)提示該額外信息
3. Using where 當(dāng)我們使用全表掃描來(lái)執(zhí)行對(duì)某個(gè)表的查詢,并且該語(yǔ)句的where 子句中有針對(duì)該表的搜索條件時(shí),在Extra列中會(huì)提示上述額外信息。當(dāng)使用索引訪問(wèn)來(lái)執(zhí)行對(duì)某個(gè)表的查詢,并且該語(yǔ)句的WHERE子句中有除了該索引包含的列之外的其他搜索條件時(shí),在`Extra '列中也會(huì)提示上述額外信息。
4. Using index 當(dāng)我們查詢列表以及搜索條件中只包含屬于某個(gè)索引的列,也就是在可以使用覆蓋索引的情況下,在Extra列將會(huì)提示該額外信息。比方說(shuō)下邊這個(gè)查詢中只需要用到‘coupon_id’而不需要回表操作:
5. Using index condition 搜索條件中出現(xiàn)了索引列,按最左匹配原則無(wú)法匹配上,但是用到了索引下推。
6. Using join buffer 在連接查詢執(zhí)行過(guò)程中,當(dāng)被驅(qū)動(dòng)表不能有效的利用索引加快訪問(wèn)速度,MySQL一般會(huì)為其分配一塊名叫`join buffer '的內(nèi)存塊來(lái)加快查詢速度,也就是我們所講的‘基于塊的嵌套循環(huán)算法’
7. Using union 表示需要進(jìn)行索引合并的索引名稱,如果出現(xiàn)了’Using union(…)提示,說(shuō)明準(zhǔn)備使用union索引合并的方式執(zhí)行查詢; 出現(xiàn)了Using sort union(…)提示,說(shuō)明準(zhǔn)備使用’sort-Union `索引合并的方式執(zhí)行查詢。
2.3 擴(kuò)展
2.3.1 最左匹配原則
最左優(yōu)先,以最左邊的為起點(diǎn)任何連續(xù)的索引都能匹配上。同時(shí)遇到范圍查詢(>、<、between、like)就會(huì)停止匹配。
為什么最左匹配:是因?yàn)閙ysql創(chuàng)建聯(lián)合索引時(shí),首先會(huì)對(duì)最左邊字段排序,也就是第一個(gè)字段,然后再在保證第一個(gè)字段有序的情況下,再排序第二個(gè)字段,以此類推。所以聯(lián)合索引最左列是絕對(duì)有序的,其他字段無(wú)序。舉個(gè)例子:可以把聯(lián)合索引看成“通訊錄”,姓名作為聯(lián)合索引,姓是第一列,名是第二列,當(dāng)查找人名時(shí),是先確定這個(gè)人姓再根據(jù)名確定人。只有名沒(méi)有姓就查不到。
2.3.2 索引下推
什么是索引下推
索引下推(Index Condition Pushdown,簡(jiǎn)稱ICP),是MySQL5.6版本的新特性,它能減少回表查詢次數(shù),提高查詢效率。MySQL服務(wù)層負(fù)責(zé)SQL語(yǔ)法解析、生成執(zhí)行計(jì)劃等,并調(diào)用存儲(chǔ)引擎層去執(zhí)行數(shù)據(jù)的存儲(chǔ)和檢索。索引下推的下推其實(shí)就是指將部分上層(服務(wù)層)負(fù)責(zé)的事情,交給了下層(引擎層)去處理。
索引下推優(yōu)化的原理
先來(lái)看一下MySQL的大致框架
不使用ICP索引掃描的過(guò)程
如上圖所示:
storage層:只將滿足index key條件的索引記錄對(duì)應(yīng)的整行記錄取出,返回給server層
server 層:對(duì)返回的數(shù)據(jù),使用后面的where條件過(guò)濾,直至返回最后一行。
使用ICP掃碼的過(guò)程
如圖所示:
storage層:首先將index key條件滿足的索引記錄區(qū)間確定,然后在索引上使用index filter進(jìn)行過(guò) 濾。將滿足的index filter條件的索引記錄才去回表取出整行記錄返回server層。不滿足index filter條件的索引記錄丟棄,不回表、也不會(huì)返回server層。
server 層:對(duì)返回的數(shù)據(jù),使用table filter條件做最后的過(guò)濾。
使用前后的成本差別
使用ICP前,存儲(chǔ)層多返回了需要被index filter過(guò)濾掉的整行記錄。使用ICP后,直接就去掉了不滿足index filter條件的記錄,省去了他們回表和傳遞到server層的成本。ICP的 加速效果 取決于在存儲(chǔ)引擎內(nèi)通過(guò) ICP篩選 掉的數(shù)據(jù)的比例。
使用ICP條件
① 只能用于二級(jí)索引(secondary index)
② explain顯示的執(zhí)行計(jì)劃中type值(join 類型)為 range 、ref 、eq_ref 或者 ref_or_null 。
③ 并非全部where條件都可以用ICP篩選,如果where條件的字段不在索引列中,還是要讀取整表的記錄到server端做where過(guò)濾。
④ ICP可以用于MyISAM和InnnoDB存儲(chǔ)引擎
⑤ MySQL 5.6版本的不支持分區(qū)表的ICP功能,5.7版本的開(kāi)始支持。
⑥ 當(dāng)SQL使用覆蓋索引時(shí),不支持ICP優(yōu)化方法
2.3.2 前綴索引
什么是前綴索引?
前綴索引也叫局部索引,比如給身份證的前 10 位添加索引,類似這種給某列部分信息添加索引的方式叫做前綴索引。
為什么要用前綴索引?
前綴索引能有效減小索引文件的大小,讓每個(gè)索引頁(yè)可以保存更多的索引值,從而提高了索引查詢的速度。但前綴索引也有它的缺點(diǎn),不能在 order by 或者 group by 中觸發(fā)前綴索引,也不能把它們用于覆蓋索引。
索引選擇性是什么?
索引的選擇性,指的是不重復(fù)的索引值(基數(shù))和表記錄數(shù)的比值。選擇性是索引篩選能力的一個(gè)指標(biāo)。索引的取值范圍是 0—1 ,當(dāng)選擇性越大,索引價(jià)值也就越大。
舉例說(shuō)明:假如有一張表格,總共有一萬(wàn)行的記錄,其中有一個(gè)性別列sex,這個(gè)列的包含選項(xiàng)就兩個(gè):男/女。那么,這個(gè)時(shí)候,這一列創(chuàng)建索引的話,索引的選擇性為萬(wàn)分之二,這時(shí)候,在性別這一列創(chuàng)建索引是沒(méi)有啥意義的。假設(shè)個(gè)極端情況,列內(nèi)的數(shù)據(jù)都是女,那么索引的選擇性為萬(wàn)分之一,其效率還不如直接進(jìn)行全表掃描。如果是主鍵索引的話,那么選擇性為1,索引價(jià)值比較大??梢灾苯痈鶕?jù)索引定位到數(shù)據(jù)。
什么情況下適合使用前綴索引?
當(dāng)字符串本身可能比較長(zhǎng),而且前幾個(gè)字符就開(kāi)始不相同,適合使用前綴索引;相反情況下不適合使用前綴索引,比如,整個(gè)字段的長(zhǎng)度為 20,索引選擇性為 0.9,而我們對(duì)前 10 個(gè)字符建立前綴索引其選擇性也只有 0.5,那么我們需要繼續(xù)加大前綴字符的長(zhǎng)度,但是這個(gè)時(shí)候前綴索引的優(yōu)勢(shì)已經(jīng)不明顯,就沒(méi)有創(chuàng)建前綴索引的必要了。
索引選擇性怎么計(jì)算?
首先我們可以通過(guò)如下 SQL 得到全列選擇性:
例如:
SELECT COUNT(DISTINCT coupon_id) / COUNT(*) FROM coupon;然后再通過(guò)如下 SQL 得到某一長(zhǎng)度前綴的選擇性:
SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name;例如:
SELECT COUNT(DISTINCT LEFT(coupon_id, 6)) / COUNT(*) FROM coupon; SELECT COUNT(DISTINCT LEFT(coupon_id, 8)) / COUNT(*) FROM coupon;總結(jié)
以上是生活随笔為你收集整理的MySQL性能分析工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 车载DSP音频项目研究开发技术的深化
- 下一篇: 云数据库PostgreSQL新人怎样上手