制作網(wǎng)站需要哪些工具互聯(lián)網(wǎng)營銷方法有哪些
MySQL 執(zhí)?計劃是 MySQL 查詢優(yōu)化器分析 SQL 查詢時?成的?份詳細(xì)計劃,包括表如何連 接、是否?索引、表掃描?數(shù)等。通過這份執(zhí)?計劃,我們可以分析這條 SQL 查詢中存在的 問題(如是否出現(xiàn)全表掃描),從?進(jìn)?針對優(yōu)化。
我們可以通過EXPLAIN
來查詢我們SQL的執(zhí)行計劃。
EXPLAIN
各字段的含義
Id
SELECT查詢的序列號,表示執(zhí)行SELECT 子句的順序(
Id相同,從上往下執(zhí)行,Id不同,值越大越先執(zhí)行。
)
select_type
查詢類型,來區(qū)分簡單查詢、聯(lián)合查詢、?查詢等。
常?的類型有:
- SIMPLE:簡單查詢,不包含表連接或?查詢
- PRIMARY:主查詢,外層的查詢
- SUBQUERY:?查詢中第?個
- SELECT UNION:UNION 后?的 SELECT 查詢語句
- UNION RESULT:UNION 合并的結(jié)果
table
查詢的表名(也可以是別名)
partitions
匹配的分區(qū),沒有分區(qū)的表為 NULL
type*
掃描表的方式。
常見的類型有:(性能從上到下,越來越差)
system
表中只有一行數(shù)據(jù)(系統(tǒng)表),這是const類型的特殊情況;
const
最多返回一條匹配的數(shù)據(jù)在查詢的最開始讀取。
因為是通過主鍵
來查詢的,然后我們的1也是常量級的,所以類型是const
。
eq_ref
在連接查詢中
被驅(qū)動表
使用主鍵或者唯一鍵
進(jìn)行連接的時候。(被驅(qū)動表
只返回一行數(shù)據(jù)),類似于外鍵查詢。
ref
在連接查詢中
被驅(qū)動表
使用普通索引
進(jìn)行連接的時候,或者在普通查詢
的WHERE條件中使用索引
,基于這個索引來匹配表中所有的行。(也就是在查詢前就知道可能會返回多條數(shù)據(jù))
fulltext
使用全文索引查詢數(shù)據(jù)。
ref_of_null
在
ref
的基礎(chǔ),額外添加了對NULL
值的查找。
在join
中也可使用
index_merge
索引合并在
key
列中會顯示所有使用到的索引。類似于有兩個條件,這兩個條件都有索引,用OR
進(jìn)行連接的話,最后會通過兩個索引查詢的所有主鍵值來進(jìn)行合并(并集
)。這個稱之為`索引合并。
![]()
key
列中,可以看見我們使用了兩個索引
range
使用索引進(jìn)行范圍查找。
像between
,>=
,>
,<
,<=
這種查詢都是范圍查詢。
like
前綴的模糊查詢也是范圍查找。
index
雖然用到了索引,但是是掃描了所有的索引。
ALL
全表掃描。(注意:全表掃描并不代表就是最差的方案,就比方你本身就需要全部表的數(shù)據(jù),你使用全表掃描還能用什么呢?)
possible_keys
這一列顯示查詢可能使用那些索引來查找。
explain 中有可能possible_keys中有值,但是我們的key中顯示NULL的情況,這種因為表中的數(shù)據(jù)不多,MySQL認(rèn)為對此查詢幫助不大,選擇了全表查詢。
key
實際采用了那個索引。
如果沒有使用索引時,我們可以通過force index
,ignore index
,來強制使用某個索引或者忽略某個索引。
key_len
表示使用
key
中索引的長度。
我們創(chuàng)建了一個b_c_d(三個字段的聯(lián)合索引)。
這里可以用的b=4
來進(jìn)行查詢。key
列中存在我們的索引,但是注意key_len
是5,代表我們使用到了部分索引。
當(dāng)我們使用b=4 and c=4
,這樣里的key_len
是10
當(dāng)我們使用b = 4 and c = 4 and d = 4
,這樣里的key_len
是15
這里的計算方式是,1個int類型的索引是4個字節(jié),又因為這個字段是允許為空的,所有的加+1位,則是5個字節(jié)。所有可以通過觀察key_len
,來判斷索引是否被充分使用。
key_len 計算規(guī)則
字符串
如果是utf-8,則一個數(shù)字與一個字符占一個字節(jié),一個漢字占3個字節(jié)
- char:如果存漢字就是3n字節(jié)
- varchar:如果存漢字則長度是3n+2字節(jié),+2的2個字節(jié)用來存儲字符串長度,因為字符串長度,
數(shù)值類型
- tinyint:1字節(jié)
- smallint:2字節(jié)
- int:4字節(jié)
- bigint:8字節(jié)
時間類型
- date:3字節(jié)
- timestamp:4字節(jié)
- datetime:8字節(jié)
注意:為空的字段,索引需要在額外+1,判斷是否為NULL;
索引最大長度
索引最大長度是768字節(jié),當(dāng)字符串過長時,mysql,會做一個類似于左前綴索引的處理,將前前半部分的字符提取出來做索引。
ref
這一列顯示了在key列記錄的索引中,表查找值所用到的列或者常量。常見的有:
const
常量,字段名
row
表示mysql大概掃描的行數(shù),這個并不是真正的結(jié)果集行數(shù)。
filtered
基于
row
掃描的行數(shù),最后用到了百分之多少的數(shù)據(jù)
,優(yōu)化可以根據(jù)這個來做文章,因為如果說有大量掃描的數(shù)據(jù)沒有被使用,那么會降低查詢效率。
Extra*
字段通?;貢@示更多的信息,可以幫助我們發(fā)現(xiàn)性能問題的所在。
Using where
使用
where
語句來進(jìn)行過濾,并且使用的**條件未被索引**
覆蓋。(表級的過濾)
Using index condition
查詢的列
沒有完全被索引覆蓋
,且使用where
條件進(jìn)行前置過濾。
Using index
表示直接通過索引即可返回所需的字段信息,不需要返回表。(
索引覆蓋
)
就比方,需要返回一個二級索引值與主鍵值,使用where條件查詢二級索引時,因為二級索引的葉子節(jié)點中存儲的是主鍵值,所有不需要進(jìn)行回表了。
Using filesort
表示需要額外的執(zhí)行排序操作。數(shù)據(jù)較小時從內(nèi)存排序,否則需要在磁盤完成排序。
Using temporart
意味著需要創(chuàng)建臨時表保存中間結(jié)果
EXPLAIN 擴展選項
EXPLAIN FORMAT = tree
按樹狀結(jié)構(gòu)輸出我們的執(zhí)行計劃。
縮進(jìn)越深越先執(zhí)行,如果縮進(jìn)相同從上往下執(zhí)行.
EXPLAIN format = tree
SELECT
*
FROMactor aLEFT JOIN country b ON a.id = b.id-> Nested loop left join (cost=1.60 rows=3)-> Table scan on a (cost=0.55 rows=3)-> Single-row index lookup on b using PRIMARY (Id=a.id) (cost=0.28 rows=1)
EXPLAIN FORMAT = json
EXPLAIN format = json
SELECT
*
FROMactor aLEFT JOIN country b ON a.id = b.id{"query_block": {"select_id": 1,"cost_info": {"query_cost": "1.60"},"nested_loop": [{"table": {"table_name": "a","access_type": "ALL","rows_examined_per_scan": 3,"rows_produced_per_join": 3,"filtered": "100.00","cost_info": {"read_cost": "0.25","eval_cost": "0.30","prefix_cost": "0.55","data_read_per_join": "456"},"used_columns": ["id","name","update_time"]}},{"table": {"table_name": "b","access_type": "eq_ref","possible_keys": ["PRIMARY"],"key": "PRIMARY","used_key_parts": ["Id"],"key_length": "4","ref": ["test.a.id"],"rows_examined_per_scan": 1,"rows_produced_per_join": 3,"filtered": "100.00","cost_info": {"read_cost": "0.75","eval_cost": "0.30","prefix_cost": "1.60","data_read_per_join": "4K"},"used_columns": ["Id","countryname","countrycode"]}}]}
}
EXPLAIN ANALYZE (MySQL8.0以上)
幫我們實際去執(zhí)行一遍,并幫我們拿到實際的執(zhí)行計劃,及實際的值。
explain ANALYZE select * from T1 join T2 on T1.a = T2.a;-> Nested loop inner join (cost=1.15 rows=2) (actual time=0.048..0.073 rows=3 loops=1)-> Covering index scan on T1 using index_b (cost=0.45 rows=2) (actual time=0.034..0.043 rows=3 loops=1)-> Single-row index lookup on T2 using PRIMARY (a=t1.a) (cost=0.30 rows=1) (actual time=0.009..0.009 rows=1 loops=3)
SHOW WARNINGS
可以拿到實際上被MySQL優(yōu)化器,優(yōu)化過后的SQL。
很經(jīng)典的樣例就是,子查詢中的Order By
被優(yōu)化掉了。
因為我們這把排序放到了子查詢內(nèi)部,執(zhí)行后發(fā)現(xiàn)我們的數(shù)據(jù)并沒有按a來進(jìn)行排序。
通過show warnings
可以看見實際執(zhí)行的SQL中并沒有Order by
方案一:在Order by 后面加個limit ,limit的數(shù)量比你原有的結(jié)果集大就行,
方案二:Order by放最外面。
MySQL針對子查詢的優(yōu)化,必須不是一個包含了limit
和order by
才會進(jìn)行優(yōu)化。