成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL中一條查詢語(yǔ)句的執(zhí)行全過(guò)程是怎樣的?

開(kāi)發(fā) 前端 MySQL
執(zhí)行計(jì)劃是我們進(jìn)行sql優(yōu)化的依賴(lài)。通過(guò)里面各個(gè)字段的信息我們能得到優(yōu)化結(jié)論,其中有些字段的含義需要我們有一些底層基礎(chǔ)比如Extra列,這一列會(huì)告訴我們當(dāng)前查詢是否走了索引,是否用了臨時(shí)表,如何進(jìn)行排序,有什么算法進(jìn)行join,這些可能需要我們?nèi)チ私馀判蛟恚?lián)表原理等等。這樣才能對(duì)sql的調(diào)優(yōu)做到游刃有余。

mysql作為最常用的關(guān)系型數(shù)據(jù)庫(kù),無(wú)論是在應(yīng)用還是在面試中都是必須掌握的技能。

要印在腦子里面的東西

DDL:數(shù)據(jù)定義,它用來(lái)定義數(shù)據(jù)庫(kù)對(duì)象,包括庫(kù),表,列,通過(guò)ddl我們可以創(chuàng)建,刪除,修改數(shù)據(jù)庫(kù)和表結(jié)構(gòu);

DML:數(shù)據(jù)操作語(yǔ)言,增加刪除修改數(shù)據(jù)表中的記錄;

DCL:數(shù)據(jù)控制語(yǔ)言,定義訪問(wèn)權(quán)限和安全級(jí)別;

DQL:數(shù)據(jù)查詢語(yǔ)言,用它來(lái)查詢想要的記錄。

SQL執(zhí)行順序:

  1. from;
  2. join
  3. on
  4. where;
  5. group by;
  6. avg,sum.... 使用聚集函數(shù)進(jìn)行計(jì)算;
  7. having;
  8. select;
  9. distinct;
  10. order by;
  11. limit;

今天我們一起討論下如何查看mysql的執(zhí)行計(jì)劃。

Explain是mysql中sql調(diào)優(yōu)的重要工具,它可以模擬mysql優(yōu)化器執(zhí)行sql語(yǔ)句,并通過(guò)可視化說(shuō)明分析出查詢語(yǔ)句的執(zhí)行信息,有助于我們分析出sql語(yǔ)句的性能瓶頸。

使用示例:

explain select * from t where name='123'

在查詢sql語(yǔ)句前面加explain關(guān)鍵字,mysql就會(huì)在查詢的時(shí)候設(shè)置一個(gè)標(biāo)記,mysql在處理的時(shí)候就不會(huì)去真正執(zhí)行這條sql語(yǔ)句,而是返回這條語(yǔ)句的執(zhí)行計(jì)劃。但是如果from中有子查詢,子查詢會(huì)真正的執(zhí)行,并且會(huì)將結(jié)果先放入臨時(shí)表中。

1準(zhǔn)備

我們準(zhǔn)備幾個(gè)表先,如果下面有需要案例來(lái)說(shuō)明的內(nèi)容,我們就用這幾個(gè)表為例。

DROP TABLE IF EXISTS `actor`;
 CREATE TABLE `actor` (
 `id` INT ( 11 ) NOT NULL,
 `name` VARCHAR ( 45 ) DEFAULT NULL,
 `update_time` datetime DEFAULT NULL,
  PRIMARY KEY ( `id` )
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO `actor` ( `id`, `name`, `update_time` )
VALUES
 ( 1, 'a', NOW() ),
 ( 2, 'b', NOW() ),
 ( 3, 'c', NOW() );
 
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `name` VARCHAR ( 10 ) DEFAULT NULL,
  PRIMARY KEY ( `id` ),
 KEY `idx_name` ( `name` )
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO `film` ( `id`, `name` )
VALUES
 ( 3, 'film0' ),
 ( 1, 'film1' ),
 ( 2, 'film2' );
 
DROP TABLE IF EXISTS `film_actor`;
 CREATE TABLE `film_actor` (
 `id` INT ( 11 ) NOT NULL,
 `film_id` INT ( 11 ) NOT NULL,
 `actor_id` INT ( 11 ) NOT NULL,
 `remark` VARCHAR ( 255 ) DEFAULT NULL,
 PRIMARY KEY ( `id` ),
 KEY `idx_film_actor_id` ( `film_id`, `actor_id` ) 
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERTINTO `film_actor` ( `id`, `film_id`, `actor_id` )
VALUES
 ( 1, 1, 1 ),
 ( 2, 1, 2 ),
 ( 3, 2, 1 );

2Explain字段說(shuō)明

執(zhí)行 explain select * from actor; 可以看到結(jié)果如下

圖片圖片

通過(guò)上圖中我們看下每個(gè)字段代表的含義

id列

id列的編號(hào)是select的序列號(hào),有幾個(gè)select就有幾個(gè)id,并且id的順序是按select出現(xiàn)的順序增長(zhǎng)的。id列越大執(zhí)行優(yōu)先級(jí)越高,id相同則從上往下執(zhí)行,id為NULL最后執(zhí)行。

select_type

select_type表示對(duì)應(yīng)行是簡(jiǎn)單查詢還是復(fù)雜的查詢。這個(gè)字段有五個(gè)值,分別代表不同的含義

通過(guò)一個(gè)查詢例證來(lái)說(shuō)明:

首先執(zhí)行下面的語(yǔ)句用來(lái)關(guān)閉mysql5.7新特性對(duì)衍生表的合并優(yōu)化:

set session optimizer_switch='derived_merge=off'

然后執(zhí)行下面語(yǔ)句看執(zhí)行過(guò)程的select_type列:

explain select (select 1 from actor where id=1) from (select * from film where id=1) t

圖片圖片

  • simple表示簡(jiǎn)單查詢,查詢不包含子查詢和union
  • primary:復(fù)雜查詢中最外層的select
  • subquery:包含在select中的子查詢(不在from子句中)
  • derived:包含在from子句中的子查詢。MySQL會(huì)將結(jié)果存放在一個(gè)臨時(shí)表中,也稱(chēng)為派生表
  • union:在union中的第二個(gè)和隨后的select
    關(guān)于union,我們通過(guò)下面語(yǔ)句來(lái)理解
explain select 1 union all select 1

圖片

table列

這一列表示explain的一行正在訪問(wèn)哪個(gè)表。

當(dāng)from子句中有子查詢時(shí),table列是< derivenN > 格式,表示當(dāng)前查詢依賴(lài)id=N的查詢,于是先執(zhí)行id=N的查詢。

當(dāng)有union時(shí),UNION RESULT的table列的值為<union1,2>,1和2表示參與union的select行id。

type

這一列表示關(guān)聯(lián)類(lèi)型或訪問(wèn)類(lèi)型,即MySQL決定如何查找表中的行,查找數(shù)據(jù)行記錄的大概范圍依次從最優(yōu)到最差分別為:system>const>eq_ref>ref>range>index>ALL一般來(lái)說(shuō),得保證查詢達(dá)到range級(jí)別,最好達(dá)到ref

列為空是因?yàn)閙ysql能夠在優(yōu)化階段分解查詢語(yǔ)句,在執(zhí)行階段用不著再訪問(wèn)表或索引。例如:在索引列中選取最小值,可以單獨(dú)查找索引來(lái)完成,不需要再進(jìn)行回表訪問(wèn)。

const:這個(gè)類(lèi)型最快,當(dāng)查詢通過(guò)優(yōu)化器優(yōu)化后可以走主鍵索引或者唯一索引(primarykey或uniquekey)的時(shí)候,這種情況只需要掃描1條數(shù)據(jù),mysql能夠迅速定位到數(shù)據(jù)。

system:system是特殊的const類(lèi)型,即當(dāng)const類(lèi)型查詢的表里面恰好只有一條數(shù)據(jù)的時(shí)候,這種概率很小,可以忽略,而且有時(shí)候即便是表里面只有一條數(shù)據(jù)執(zhí)行計(jì)劃中看到的也是const類(lèi)型,這個(gè)不用太糾結(jié)。

舉個(gè)例子看下:

explain select * from (select * from actor where id = 1) t;

圖片圖片

eq_ref:上面的兩種類(lèi)型是主鍵索引或者唯一索引(primarykey或uniquekey)查詢,并且最多只有一條記錄匹配,而eq_ref類(lèi)型說(shuō)的是同樣是主鍵索引或者唯一索引(primarykey或uniquekey)查詢,但是返回的是多條數(shù)據(jù),比如下面例子:聯(lián)表查詢的時(shí)候

explain select * from film_actor left join film on film_actor.film_id=film.id

圖片圖片

這是一種主鍵索引或者唯一索引來(lái)進(jìn)行聯(lián)表的方式。也在const之外最好的聯(lián)接類(lèi)型了,簡(jiǎn)單的select查詢不會(huì)出現(xiàn)這種type

ref:相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個(gè)值相比較,可能會(huì)找到多個(gè)符合條件的行。

1.簡(jiǎn)單select查詢,name是普通索引(非唯一索引)

explain select * from film where name='film1';

圖片圖片

2.關(guān)聯(lián)表查詢,idx_film_actor_id是film_id和actor_id的聯(lián)合索引,這里使用到了film_actor的左邊前綴film_id部分

explain select film_id from film left join film_actor on film.id=film_actor.film_id

圖片圖片

range:索引范圍掃描,通常出現(xiàn)在in(),between,>,<,>=等操作中。使用一個(gè)索引來(lái)檢索給定范圍的行

explain select * from actor where id>1

index:全索引掃描就能拿到結(jié)果,一般是掃描某個(gè)二級(jí)索引,這種掃描不會(huì)從索引樹(shù)根節(jié)點(diǎn)開(kāi)始快速查找,而是直接對(duì)二級(jí)索引的葉子節(jié)點(diǎn)遍歷和掃描,速度還是比較慢的,這種查詢一般為使用覆蓋索引,二級(jí)索引一般比較小,所以這種通常比ALL快一些。

ALL:即全表掃描,掃描你的聚簇索引的所有葉子節(jié)點(diǎn)。這是最慢的一種查詢類(lèi)型,通常情況下這需要增加索引來(lái)進(jìn)行優(yōu)化了。

possible_keys列

這一列顯示查詢可能使用哪些索引來(lái)查找。explain時(shí)可能出現(xiàn)possible_keys有列,而key顯示NULL的情況,這種情況是因?yàn)楸碇袛?shù)據(jù)不多,mysql認(rèn)為索引對(duì)此查詢幫助不大,選擇了全表查詢。如果該列是NULL,則沒(méi)有相關(guān)的索引。

key列

這一列顯示mysql實(shí)際采用哪個(gè)索引來(lái)優(yōu)化對(duì)該表的訪問(wèn)。如果沒(méi)有使用索引,則該列是NULL。如果想強(qiáng)制mysql使用或忽視possible_keys列中的索引,在查詢中使用forceindex、ignoreindex

key_len列

這一列顯示了mysql在索引里使用的字節(jié)數(shù),通過(guò)這個(gè)值可以算出具體使用了索引中的哪些列。

舉例來(lái)說(shuō),film_actor的聯(lián)合索引idx_film_actor_id由film_id和actor_id兩個(gè)int列組成,并且每個(gè)int是4字節(jié)。通過(guò)結(jié)果中的key_len=4可推斷出查詢使用了第一個(gè)列:film_id列來(lái)執(zhí)行索引查找。

explain select * from film_actor  where film_id=2;

圖片圖片

key_len計(jì)算規(guī)則如下:

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符數(shù),而不是字節(jié)數(shù),如果是utf-8,一個(gè)數(shù)字或字母占1個(gè)字節(jié),一個(gè)漢字占3個(gè)字節(jié)。

char(n):如果存漢字長(zhǎng)度就是3n字節(jié)

varchar(n):如果存漢字則長(zhǎng)度是3n+2字節(jié),加的2字節(jié)用來(lái)存儲(chǔ)字符串長(zhǎng)度,因?yàn)関archar是變長(zhǎng)字符串

  • 數(shù)值類(lèi)型

tinyint:1字節(jié)

smallint:2字節(jié)

int:4字節(jié)

bigint:8字節(jié)

  • 時(shí)間類(lèi)型

date:3字節(jié)

timestamp:4字節(jié)

datetime:8字節(jié)

  • 如果字段允許為NULL,需要1字節(jié)記錄是否為NULL,索引最大長(zhǎng)度是768字節(jié),當(dāng)字符串過(guò)長(zhǎng)時(shí),mysql會(huì)做一個(gè)類(lèi)似左前綴索引的處理,將前半部分的字符提取出來(lái)做索引。

ref列

這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見(jiàn)的有:const(常量),字段名(例:film.id)

rows列

這一列是mysql估計(jì)要讀取并檢測(cè)的行數(shù),不一定是最終查詢所要掃描的行數(shù),更不是結(jié)果集里的行數(shù)。

這里順便說(shuō)一下rows數(shù)值是怎么得到的

MySQL在真正開(kāi)始執(zhí)行語(yǔ)句之前,并不能精確地知道滿足這個(gè)條件的記錄有多少條,而只能根據(jù)統(tǒng)計(jì)信息來(lái)估算記錄數(shù)。這個(gè)統(tǒng)計(jì)信息就是索引的“區(qū)分度”。顯然,一個(gè)索引上不同的值越多,這個(gè)索引的區(qū)分度就越好。而一個(gè)索引上不同的值的個(gè)數(shù),我們稱(chēng)之為“基數(shù)”。也就是說(shuō),這個(gè)基數(shù)越大,索引的區(qū)分度越好。那么這個(gè)基數(shù)是怎么來(lái)的呢?這個(gè)基數(shù)是通過(guò)采樣統(tǒng)計(jì)來(lái)的,為什么要采樣統(tǒng)計(jì)呢?因?yàn)榘颜麖埍砣〕鰜?lái)一行行統(tǒng)計(jì),雖然可以得到精確的結(jié)果,但是代價(jià)太高了,所以只能選擇采樣,采樣統(tǒng)計(jì)的時(shí)候,InnoDB默認(rèn)會(huì)選擇N個(gè)數(shù)據(jù)頁(yè),統(tǒng)計(jì)這些頁(yè)面上的不同值,得到一個(gè)平均值,然后乘以這個(gè)索引的頁(yè)面數(shù),就得到了這個(gè)索引的基數(shù)。然后再根據(jù)這個(gè)基數(shù)得到預(yù)估行數(shù)。

但是數(shù)據(jù)表是會(huì)持續(xù)更新的,索引統(tǒng)計(jì)信息也不會(huì)固定不變。所以,當(dāng)變更的數(shù)據(jù)行數(shù)超過(guò)1/M的時(shí)候,會(huì)自動(dòng)觸發(fā)重新做一次索引統(tǒng)計(jì)。在MySQL中,有兩種存儲(chǔ)索引統(tǒng)計(jì)的方式,可以通過(guò)設(shè)置參數(shù)innodb_stats_persistent的值來(lái)選擇:

設(shè)置為on的時(shí)候,表示統(tǒng)計(jì)信息會(huì)持久化存儲(chǔ)。這時(shí),默認(rèn)的N是20,M是10。

設(shè)置為off的時(shí)候,表示統(tǒng)計(jì)信息只存儲(chǔ)在內(nèi)存中。這時(shí),默認(rèn)的N是8,M是16。

由于是采樣統(tǒng)計(jì),所以不管N是20還是8,這個(gè)基數(shù)都是很容易不準(zhǔn)的。

總之,基數(shù)小,區(qū)分度小,掃描行數(shù)就相對(duì)多,基數(shù)大,區(qū)分度大,掃描行數(shù)就相對(duì)少。

Extra列

這一列展示的是額外信息。常見(jiàn)的重要值如下:

1.Using index:使用覆蓋索引

mysql執(zhí)行計(jì)劃explain結(jié)果里的key有使用索引,如果select后面查詢的字段都可以從這個(gè)索引的樹(shù)中獲取,這種情況一般可以說(shuō)是用到了覆蓋索引,extra里一般就會(huì)有using index。

覆蓋索引一般針對(duì)的是輔助索引,整個(gè)查詢結(jié)果只通過(guò)輔助索引就能拿到結(jié)果,不需要通過(guò)輔助索引樹(shù)找到主鍵,再通過(guò)主鍵去主鍵索引樹(shù)里獲取其它字段值。

explain select film_id from film_actor where film_id=1;

圖片圖片

2.Using where:使用where語(yǔ)句來(lái)處理結(jié)果,并且查詢的列未被索引覆蓋

explain select * from actor where name='a'

圖片圖片

3.Using index condition:查詢的列不完全被索引覆蓋,where條件中是一個(gè)前導(dǎo)列的范圍

就是應(yīng)用了索引的最左前綴原則

explain select * from film_actor where film_id>1

4.Using temporary:mysql需要?jiǎng)?chuàng)建一張臨時(shí)表來(lái)處理查詢。

出現(xiàn)這種情況一般是要進(jìn)行優(yōu)化的,首先是想到用索引來(lái)優(yōu)化。

actor.name沒(méi)有索引,此時(shí)創(chuàng)建了張臨時(shí)表來(lái)distinct

explain select distinct name from actor;

5.Using filesort:將用外部排序而不是索引排序

數(shù)據(jù)較小時(shí)從內(nèi)存排序,否則需要在磁盤(pán)完成排序。這種情況下一般也是要考慮使用索引來(lái)優(yōu)化的。

1.actor.name未創(chuàng)建索引,會(huì)瀏覽actor整個(gè)表,保存排序關(guān)鍵字name和對(duì)應(yīng)的id,然后排序name并檢索

explain select * from actor order by name;

圖片

6.Using join buffer(Block Nested Loop):join語(yǔ)句相關(guān)

當(dāng)join語(yǔ)句中的被驅(qū)動(dòng)表沒(méi)有索引時(shí)候會(huì)走Block Nested Loop算法,這種情況就會(huì)把驅(qū)動(dòng)表的數(shù)據(jù)全部放入join buffer內(nèi)存中,然后進(jìn)行匹配,后面我們會(huì)詳細(xì)介紹join原理

7.Using MRR:這是一種回表優(yōu)化

mysql在做查詢的時(shí)候,我們知道當(dāng)應(yīng)用到二級(jí)索引的時(shí)候會(huì)存在回表現(xiàn)象,你想一下,索引是有序的,當(dāng)我們通過(guò)二級(jí)索引查到主鍵,再根據(jù)主鍵去主鍵索引樹(shù)查找數(shù)據(jù)的時(shí)候,用主鍵索引在主鍵樹(shù)查找的這個(gè)動(dòng)作是隨機(jī)讀,我們知道隨機(jī)讀肯定沒(méi)有順序讀快,因此MRR算法就是解決這個(gè)問(wèn)題的。這個(gè)我們后續(xù)會(huì)詳細(xì)講解。

3總結(jié)

執(zhí)行計(jì)劃是我們進(jìn)行sql優(yōu)化的依賴(lài)。通過(guò)里面各個(gè)字段的信息我們能得到優(yōu)化結(jié)論,其中有些字段的含義需要我們有一些底層基礎(chǔ)比如Extra列,這一列會(huì)告訴我們當(dāng)前查詢是否走了索引,是否用了臨時(shí)表,如何進(jìn)行排序,有什么算法進(jìn)行join,這些可能需要我們?nèi)チ私馀判蛟?,?lián)表原理等等。這樣才能對(duì)sql的調(diào)優(yōu)做到游刃有余。

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)本農(nóng)
相關(guān)推薦

2024-12-17 06:20:00

MySQLSQL語(yǔ)句數(shù)據(jù)庫(kù)

2023-11-01 16:50:58

2022-02-11 14:43:53

SQL語(yǔ)句C/S架構(gòu)

2024-07-29 09:49:00

SQLMySQL執(zhí)行

2021-06-07 08:37:03

SQL 查詢語(yǔ)句

2020-07-03 07:39:45

查詢語(yǔ)句

2011-04-18 15:56:10

軟件測(cè)試

2021-08-03 08:41:18

SQLMysql面試

2011-02-22 10:46:02

Samba配置

2024-07-16 08:31:41

2021-08-30 05:47:12

MySQL SQL 語(yǔ)句數(shù)據(jù)庫(kù)

2017-04-25 18:03:11

Caffe深度學(xué)習(xí)框架

2010-06-11 13:15:07

UML軟件

2024-09-09 08:15:20

2009-12-08 17:56:16

WCF配置

2009-04-13 12:37:18

2011-09-06 15:38:20

QT安裝

2011-01-21 17:51:52

2009-02-20 10:25:54

UML軟件設(shè)計(jì)例程

2011-08-15 09:19:22

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: www.伊人.com | 午夜av一区二区 | 在线视频 亚洲 | 日韩免| 中文字幕一区二区三区乱码图片 | 久久99精品久久久久久国产越南 | 一级做受毛片免费大片 | 日本h片在线观看 | 九九热精品在线视频 | 久久精品中文字幕 | 国产乱码精品一区二区三区五月婷 | 最新超碰| 日韩黄色av | 久久国产婷婷国产香蕉 | 精品亚洲一区二区三区 | 成人亚洲网 | 一区二区三区免费 | 国内精品久久影院 | 精品久久久网站 | 日韩区 | 自拍第一页 | 久久久久久久久久久久久久国产 | 日本h片在线观看 | 国产欧美在线一区二区 | 亚洲情侣视频 | 一级做a爰片久久毛片 | 日韩福利 | 亚洲午夜在线 | av网站免费观看 | 九九热re | 岛国av免费在线观看 | 99re6在线| 中文字幕中文字幕 | 国产精品久久久久久久久大全 | 午夜私人影院在线观看 | 91视视频在线观看入口直接观看 | 中文区中文字幕免费看 | 欧美综合一区二区 | 午夜羞羞 | 国产成人jvid在线播放 | 国产精品一区久久久久 |