一條select語(yǔ)句嚷节,Mysql的執(zhí)行過程

當(dāng)我們執(zhí)行一條select語(yǔ)句后,究竟Mysql做了些什么虎锚?整個(gè)過程其實(shí)可以涉及到Mysql的組成結(jié)構(gòu)硫痰,Innodb的頁(yè)結(jié)構(gòu)以及Mysql的索引原理。本文中我們會(huì)一一講到:
首先這個(gè)問題會(huì)涉及到Mysql的組成結(jié)構(gòu):


Mysql組成結(jié)構(gòu)

很清晰可以看見Mysql的組成結(jié)構(gòu)包括3個(gè)主要部分:客戶端(Client)窜护、服務(wù)端(Server)效斑、執(zhí)行引擎(Engine)。用戶就是通過客戶端輸入sql指令柱徙,其實(shí)另一種說(shuō)法Client不算是Mysql的組成部分缓屠,比如我們知道的Mysql Workbench,命令行工具护侮,它們不算Mysql的一部分藏研,我這里把它放進(jìn)來(lái),是希望讀者可以整體的理解這個(gè)結(jié)構(gòu)概行,客戶端算與不算不影響討論實(shí)際問題蠢挡。服務(wù)端會(huì)接受客戶端來(lái)的數(shù)據(jù),然后對(duì)其進(jìn)行緩存查詢凳忙,詞義語(yǔ)義的分析业踏,sql語(yǔ)句優(yōu)化等等,最終通過執(zhí)行器找存儲(chǔ)引擎對(duì)磁盤上的表進(jìn)行操作涧卵。這就是Mysql整體的觀感勤家,之后我們會(huì)對(duì)各個(gè)部分進(jìn)行詳細(xì)的解釋:

連接器:

顧名思義是用于Mysql和客戶端進(jìn)行連接,當(dāng)我們?cè)趖erminal中輸入:mysql -u username -p后柳恐,Mysql客戶端就會(huì)跟你服務(wù)器上的Mysql進(jìn)行友好的TCP三次握手伐脖,雙方狀態(tài)都變?yōu)閑stablished之后热幔,連接器開始驗(yàn)證你輸入的用戶名和密碼;

連接器處理連接請(qǐng)求

連接器在處理連接請(qǐng)求的時(shí)候讼庇,除了會(huì)驗(yàn)證用戶名密碼以外绎巨,還會(huì)去檢驗(yàn)用戶權(quán)限,讀者可以通過以下方式查看Mysql中記錄的用戶權(quán)限:

  1. root用戶登陸
mysql -uroot -p
  1. 進(jìn)入系統(tǒng)默認(rèn)的名字為“mysql”的數(shù)據(jù)庫(kù)
use mysql;
  1. 在“mysql”數(shù)據(jù)庫(kù)中存在user表(show tables)
select * from user \G
用戶權(quán)限

用戶權(quán)限如上圖所示蠕啄,對(duì)于peita1這個(gè)user场勤,在數(shù)據(jù)庫(kù)subscribe_message_microservice中select權(quán)限、insert權(quán)限……都可以看到了歼跟。
這個(gè)權(quán)限被連接器拿到后會(huì)記錄下來(lái)和媳,這意味著在這一次數(shù)據(jù)庫(kù)連接的過程中,peita1用戶的權(quán)限就是如此了哈街,如果此時(shí)root用戶取消了peita1用戶對(duì)某數(shù)據(jù)庫(kù)的insert權(quán)限留瞳,在此次數(shù)據(jù)庫(kù)連接沒有斷開的情況下,peita1的insert權(quán)限不會(huì)受到影響骚秦。

查詢緩存:

首先聲明Mysql8.0之后查詢緩存模塊被拿掉了她倘,查詢緩存僅對(duì)于select語(yǔ)句,當(dāng)查詢緩存開啟的時(shí)候骤竹,比如對(duì)表A的select * from A;該語(yǔ)句和查詢結(jié)果會(huì)以HashMap的形式帝牡,將查詢語(yǔ)句(select * from A)作為key,結(jié)果作為value存儲(chǔ)起來(lái)蒙揣,當(dāng)你下一次進(jìn)行查詢的select查詢的時(shí)候靶溜,如果是一模一樣的查詢語(yǔ)句,則會(huì)命中懒震,且Mysql不往下執(zhí)行罩息,直接返回結(jié)果。當(dāng)然即便A表的查詢經(jīng)過緩存个扰,但是任意時(shí)候?qū)表進(jìn)行了增刪改操作瓷炮,這條與A表相關(guān)的緩存也會(huì)被清空。這里就有一個(gè)問題递宅!在查詢緩存打開的清空下娘香,每一張表進(jìn)行了增刪改之后都要檢查緩存,看看是否需要?jiǎng)h除記錄办龄,這對(duì)相應(yīng)的操作性能有影響烘绽,所以看起來(lái)這是一個(gè)不錯(cuò)的功能,但并不推薦使用俐填,更絕的是8.0之后就直接拿掉了安接。
我們就以查詢語(yǔ)句:select * from food where id = 3;為例英融,來(lái)看一看這樣的SQL語(yǔ)句是如何執(zhí)行的盏檐。

分析器

如果查詢緩存關(guān)閉或是沒有命中緩存的情況下歇式,SQL語(yǔ)句會(huì)進(jìn)入分析器,分析器做兩個(gè)事:一是詞義分析胡野,也就是說(shuō)根據(jù)你這個(gè)SQL字符和空格組成的字符串材失,對(duì)關(guān)鍵字進(jìn)行分析,比如發(fā)現(xiàn)有“select”给涕,Mysql就知道這是一個(gè)查詢語(yǔ)句豺憔,發(fā)現(xiàn)from后面的“food”额获,就知道food是表名够庙;在確定了這個(gè)SQL字符串是來(lái)干嘛的之后,就開始第二件事抄邀,語(yǔ)義分析耘眨,這就是我們熟悉的語(yǔ)法檢查,一旦發(fā)現(xiàn)錯(cuò)誤就報(bào)出:You have an error in your sql syntax境肾。


分析器的兩個(gè)步驟

優(yōu)化器:

優(yōu)化器會(huì)在連表查詢的時(shí)候確定怎樣的查詢順序比較好剔难,或是有多個(gè)索引的時(shí)候決定用哪一個(gè)索引,不用哪一個(gè)索引等等奥喻,它會(huì)根據(jù)執(zhí)行的效率進(jìn)行判斷偶宫。當(dāng)優(yōu)化器決定了最終的執(zhí)行方案后,就會(huì)交由執(zhí)行器進(jìn)行執(zhí)行环鲤。

執(zhí)行器:

在執(zhí)行select * from food where id = 3之前纯趋,執(zhí)行器會(huì)先判斷當(dāng)前的登陸用戶是否有權(quán)限訪問user這個(gè)表。根據(jù)優(yōu)化器的分析如果是全表掃描的話就會(huì)調(diào)用InnoDB執(zhí)行引擎調(diào)取第一行冷离,記錄之后調(diào)取“下一行”接口……直至找到吵冒;如果是通過索引查找,下文會(huì)詳細(xì)介紹查找過程西剥,最后執(zhí)行器將所有結(jié)果的集合返回給用戶痹栖。


數(shù)據(jù)頁(yè)結(jié)構(gòu)

當(dāng)目前為止這個(gè)SQL語(yǔ)句算是走完了Mysql的Server端,進(jìn)而來(lái)到了存儲(chǔ)引擎瞭空,從版本5.5.5之后揪阿,Mysql默認(rèn)的存儲(chǔ)引擎就是InnoDB,這里我們就以它為例咆畏,進(jìn)行介紹南捂。上面我們說(shuō)了,執(zhí)行器在查詢SQL語(yǔ)句的時(shí)候鳖眼,會(huì)調(diào)InnoDB的接口一行一行查詢黑毅,這里有一個(gè)問題,InnoDB當(dāng)中是如何執(zhí)行的呢钦讳?如果查詢條件中有索引呢矿瘦?為了更準(zhǔn)確的描述這條語(yǔ)句的執(zhí)行枕面,我們先了解一下,InnoDB中數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)和索引的實(shí)現(xiàn)原理缚去。
InnoDB當(dāng)中數(shù)據(jù)是按照頁(yè)的方式進(jìn)行存儲(chǔ)潮秘,我們看到下圖,就是InnoDB將數(shù)據(jù)按照頁(yè)的結(jié)構(gòu)進(jìn)行存儲(chǔ)易结。暫時(shí)分不清沒關(guān)系枕荞,我們看到User Records就是用戶的數(shù)據(jù),在它下面Free Space字面意思是空閑空間搞动,其實(shí)可以理解成User Records的備胎空間躏精,新一條數(shù)據(jù)存儲(chǔ)進(jìn)來(lái)就從Free Space當(dāng)中拿一點(diǎn)空間來(lái)給到User Records。

數(shù)據(jù)頁(yè)結(jié)構(gòu)

好鹦肿!既然User Records的存放用戶數(shù)據(jù)知道了矗烛,那么在這里面存放的用戶數(shù)據(jù)是不是我們平時(shí)看到的Mysql表中一行行的數(shù)據(jù)呢?
Mysql當(dāng)中的表數(shù)據(jù)

不完全是箩溃!User Records當(dāng)中數(shù)據(jù)的確是以行為單位進(jìn)行存儲(chǔ)瞭吃,但存儲(chǔ)格式如下所示:
Compact行格式

其中變長(zhǎng)字段長(zhǎng)度列表指的是:類似于varchar這樣的變長(zhǎng)字段,在該字段保存著實(shí)際的長(zhǎng)度值涣旨;
NULL標(biāo)志位:對(duì)于值為null的字段歪架,不會(huì)記錄到后面的列數(shù)據(jù)當(dāng)中,而是放在null標(biāo)志位霹陡;
記錄頭信息后面遇到了再進(jìn)行介紹和蚪,行結(jié)構(gòu)的詳細(xì)理解可以參見
我們?cè)趧?chuàng)建數(shù)據(jù)庫(kù)的時(shí)候默認(rèn)的行格式為Dynamic,我們可以通過執(zhí)行show table status where name = 'table name' \G看到對(duì)應(yīng)的Row_Format:
查看table信息

那么Dynamic和Compact格式的區(qū)別這里解釋一下:
數(shù)據(jù)頁(yè)中穆律,一行數(shù)據(jù)可以存放的大小是65535個(gè)字節(jié)惠呼,那么一個(gè)數(shù)據(jù)頁(yè)可以存放多少字節(jié)呢?執(zhí)行:show global status like 'Innodb_page_size';可見是16384個(gè)字節(jié)大小峦耘。
數(shù)據(jù)頁(yè)默認(rèn)大小

因此會(huì)出現(xiàn)行大小大于頁(yè)容量的行溢出情況剔蹋,Compact行格式和Dynamic行格式對(duì)于這個(gè)地方的處理不一樣:
![Compact和Dynamic對(duì)行溢出的處理]!(https://upload-images.jianshu.io/upload_images/4995250-74d7787b41fc5510.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以看到Compact和Dynamic對(duì)于行溢出的處理的不同,在第一頁(yè)Compact存放的是行數(shù)據(jù)的第一部分和下一部分的地址辅髓;而在Dynamic行格式的第一頁(yè)存放的僅僅是第一部分的地址泣崩,而不會(huì)存放數(shù)據(jù),這樣做有一個(gè)好處洛口,因?yàn)榈谝豁?yè)只存放地址矫付,需要的行空間很小,這就意味著第一頁(yè)可以存放更多的行第焰,而這些行其實(shí)后面就是要講的索引的目錄买优!


索引原理

數(shù)據(jù)頁(yè)結(jié)構(gòu)還有一些沒有解釋的字段,我會(huì)放在后面,用到的時(shí)候進(jìn)行描述杀赢,這樣理解起來(lái)更生動(dòng)烘跺。
我們講到查詢,在Mysql中避不開索引脂崔,在印象當(dāng)中有索引的情況下滤淳,查詢效率會(huì)變高,如果問起原因砌左,可能有人會(huì)說(shuō)B+樹查找脖咐,本文準(zhǔn)備用倒推的方式,從現(xiàn)象倒推出B+的形成過程汇歹,我想這樣一來(lái)屁擅,你對(duì)于為什么是B+樹有更好的理解!我們先看這樣一個(gè)現(xiàn)象:執(zhí)行下面的SQL語(yǔ)句秤朗,我們發(fā)現(xiàn)數(shù)據(jù)展示和數(shù)據(jù)導(dǎo)入的順序不一樣煤蹭。

create table tab_1 (id integer primary key, name varchar(20));
insert into tab_1 (id, name) values (3,'tony'), (2, 'wang'), (8, 'zhang'), (6, 'lily');
select * from tab_1;

tab_1

我們看到主鍵這一列笔喉,雖然我輸入的時(shí)候順序是3-2-8-6(對(duì)照insert語(yǔ)句)取视,但是查詢的時(shí)候,發(fā)現(xiàn)Mysql常挚,其實(shí)是InnoDB已經(jīng)幫我排好了序作谭,這樣做當(dāng)然有很大的好處,比如在做范圍查詢的時(shí)候奄毡,就可以很快速的進(jìn)行定位折欠!
現(xiàn)在問題來(lái)了,為什么InnoDB會(huì)在我們插入后吼过,將主鍵排序锐秦?它是怎么做到的?首先我們注意一點(diǎn)盗忱,自定義主鍵是默認(rèn)具備索引功能的酱床,如果要實(shí)現(xiàn)這樣的功能,我想應(yīng)該有一個(gè)鏈表一樣的趟佃,對(duì)于鏈表扇谣,我們要輸入3-2-8-6過程如下:
鏈表輸入數(shù)據(jù)

我們知道對(duì)于鏈表來(lái)說(shuō)上面所提到的插入不是難事,但是如果需要對(duì)這種存儲(chǔ)方式進(jìn)行搜索闲昭,就麻煩了罐寨,它沒有數(shù)組連續(xù)存儲(chǔ)的特點(diǎn),無(wú)法快速定位序矩。這時(shí)候我們就聯(lián)系到實(shí)際的數(shù)據(jù)頁(yè)來(lái)進(jìn)行優(yōu)化鸯绿。

  1. 引入頁(yè)內(nèi)目錄
    如果我們想書的目錄一樣,在我們頁(yè)內(nèi)也設(shè)置一個(gè)目錄,這個(gè)目錄可以有效的劃分我們存儲(chǔ)的索引id瓶蝴,那么我們?cè)诓樵兊臅r(shí)候可以先根據(jù)頁(yè)內(nèi)目錄確定范圍幔烛,然后進(jìn)行查詢。下圖所示囊蓝,我們將頁(yè)內(nèi)數(shù)據(jù)兩兩分組饿悬,將每組最小索引值拿出來(lái)作為我們目錄的key,value自然就是存放這個(gè)最小節(jié)點(diǎn)的內(nèi)存引用了聚霜〗铺瘢可以看到,如果我們要訪問8蝎宇,就可以先通過目錄6節(jié)點(diǎn)弟劲,找到鏈表的下半部分,然后再開始找next節(jié)點(diǎn)姥芥。


    頁(yè)內(nèi)目錄
  2. 引入頁(yè)目錄
    另一個(gè)問題隨之而來(lái)兔乞,如果數(shù)據(jù)越來(lái)越多,數(shù)據(jù)需要分頁(yè)存放了凉唐,自然庸追,頁(yè)面之間的關(guān)聯(lián)需要有地方存儲(chǔ),數(shù)據(jù)頁(yè)需要知道上一頁(yè)和下一頁(yè)在哪里台囱。另外既然頁(yè)內(nèi)目錄可以方便定位淡溯,現(xiàn)在頁(yè)間豈不是頁(yè)應(yīng)該有個(gè)目錄,不然當(dāng)我拿到15這個(gè)索引值的時(shí)候簿训,我并不知道去哪一個(gè)頁(yè)面找數(shù)據(jù)呀咱娶,所以在頁(yè)外面還有一層目錄,當(dāng)我們要查找13的時(shí)候强品,首先通過頁(yè)目錄找到了地址為300的頁(yè)膘侮,然后進(jìn)去找頁(yè)內(nèi)目錄10,進(jìn)而找到鏈表上半部分的榛,找到13琼了。


    頁(yè)目錄

    大家看這是個(gè)啥!不就是B+樹的數(shù)據(jù)結(jié)構(gòu)嗎困曙!所以為什么說(shuō)Mysql索引的底層是B+樹實(shí)現(xiàn)表伦,包括B+樹實(shí)現(xiàn)有什么好處,一目了然慷丽。
    這里補(bǔ)充兩點(diǎn)蹦哼,對(duì)于主鍵索引,它在B+樹上的葉子節(jié)點(diǎn)上存的key是索引值(也就是主鍵值)要糊,那么value是什么呢纲熏?value就是整行的數(shù)據(jù)。另外對(duì)于上面這個(gè)B+樹,葉子節(jié)點(diǎn)當(dāng)然還有點(diǎn)不完整的地方局劲,那就是勺拣,葉子節(jié)點(diǎn)實(shí)際上是雙向鏈表,而不是單向的鱼填,這也是方便我們進(jìn)行范圍查詢药有,當(dāng)我們需要找小于28的內(nèi)容時(shí),Mysql會(huì)先定位到28苹丸,然后根據(jù)prev指針愤惰,往前找。


    B+樹構(gòu)建索引底層

這里我們要注意一點(diǎn)并不是有索引就一定會(huì)通過索引查詢赘理,優(yōu)化器會(huì)判斷索引查找和全表查找的效率宦言。本文就不再贅述∩棠#總之以上就是一個(gè)查詢語(yǔ)句在Mysql的Server端和InnoDB的整個(gè)過程奠旺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市施流,隨后出現(xiàn)的幾起案子响疚,更是在濱河造成了極大的恐慌,老刑警劉巖嫂沉,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稽寒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡趟章,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門慎王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚓土,“玉大人,你說(shuō)我怎么就攤上這事赖淤∈衿幔” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵咱旱,是天一觀的道長(zhǎng)确丢。 經(jīng)常有香客問我,道長(zhǎng)吐限,這世上最難降的妖魔是什么鲜侥? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮诸典,結(jié)果婚禮上描函,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好舀寓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布胆数。 她就那樣靜靜地躺著,像睡著了一般互墓。 火紅的嫁衣襯著肌膚如雪必尼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天篡撵,我揣著相機(jī)與錄音胰伍,去河邊找鬼。 笑死酸休,一個(gè)胖子當(dāng)著我的面吹牛骂租,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斑司,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渗饮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宿刮?” 一聲冷哼從身側(cè)響起互站,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僵缺,沒想到半個(gè)月后胡桃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡磕潮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年翠胰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片自脯。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡之景,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膏潮,到底是詐尸還是另有隱情锻狗,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布焕参,位于F島的核電站轻纪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叠纷。R本人自食惡果不足惜刻帚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讲岁。 院中可真熱鬧我擂,春花似錦衬以、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至衙吩,卻和暖如春互妓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坤塞。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工冯勉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摹芙。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓灼狰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親浮禾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子交胚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容