MySQL select實(shí)現(xiàn)原理

工作中需要借鑒MySQL對(duì)于select的具體實(shí)現(xiàn),在網(wǎng)上搜了很久,幾乎都是介紹原理的多艇,對(duì)于實(shí)現(xiàn)細(xì)節(jié)都沒(méi)有介紹,無(wú)奈之下只得自己對(duì)著源碼gdb像吻。結(jié)合以前對(duì)于sql解析的了解峻黍,對(duì)mysql select的具體實(shí)現(xiàn)有了大致的了解,總結(jié)一下拨匆。

如果要gdb單步調(diào)試姆涩,需要在編譯MySQl時(shí)加上debug選項(xiàng),參見(jiàn)這篇博客.編譯好以后就可以用gdb啟動(dòng)了惭每。如果希望mysql運(yùn)行時(shí)有日志輸出骨饿,可以指定輸出文件的路徑和日志類型:--debug=d,info,error,query,enter,general,where:O,/tmp/mysqld.trace日志對(duì)MySQl內(nèi)部邏輯的了解還是挺有用的。

MySQl在設(shè)計(jì)時(shí),采用了這樣的思路:針對(duì)主要應(yīng)用場(chǎng)景選擇一個(gè)或幾個(gè)性能優(yōu)異的核心算法作為引擎,然后努力將一些非主要應(yīng)用場(chǎng)景作為該算法的特例或變種植入到引擎當(dāng)中。具體而言,MySQL的select查詢中贷祈,核心功能就是JOIN查詢,因此在設(shè)計(jì)時(shí),核心實(shí)現(xiàn)JOIN功能,對(duì)于其它功能,都通過(guò)轉(zhuǎn)換為JOIN來(lái)實(shí)現(xiàn)储藐。

比如select id, name from student;俱济,MySQL在執(zhí)行時(shí),也會(huì)轉(zhuǎn)換為JOIN來(lái)操作钙勃。

用gdb單步跟蹤后可以看出MySQL的執(zhí)行過(guò)程大致如下:

  1. 收到請(qǐng)求后分配線程處理蛛碌;
  2. sql解析,MySQL解析完sql以后辖源,會(huì)生成很多item類蔚携。item類是sql解析和執(zhí)行中最重要的類之一,對(duì)于它的介紹可以參見(jiàn)這里克饶。
  3. 執(zhí)行sql酝蜒,可以看到JOIN::exec,MySQL是將任何select都轉(zhuǎn)換為JOIN來(lái)處理的矾湃。

以sql:select A.id, B.score from student A left join subject B on A.id=B.id where A.age > 10 and B.score > 60;為例來(lái)說(shuō)明上面的步驟3的具體過(guò)程亡脑。

首先,MySQL在執(zhí)行sql之前,會(huì)對(duì)sql進(jìn)行優(yōu)化處理霉咨,具體是在JOIN::optimise函數(shù)中完成蛙紫。MySQL針對(duì)JOIN的優(yōu)化做的非常好,因此才會(huì)將其他操作都轉(zhuǎn)換為性能實(shí)現(xiàn)的非常好的JOIN操作途戒。對(duì)于上面的sql坑傅,MySQL在執(zhí)行時(shí),會(huì)將join的key也轉(zhuǎn)換為一個(gè)where條件:A.id=B.id來(lái)執(zhí)行喷斋,那么經(jīng)過(guò)處理后唁毒,上面的sql就有了3個(gè)where條件:

  1. A.age > 10
  2. A.id = B.id继准;
  3. B.score > 60枉证;

預(yù)處理完以后開(kāi)始執(zhí)行,即JOIN::exec函數(shù)移必,首先會(huì)調(diào)用send_fields函數(shù)室谚,將最終結(jié)果的信息返回,然后調(diào)用do_select崔泵。MySQL的join是采用nested loop join秒赤,可以參見(jiàn)這篇博客。在do_select函數(shù)中憎瘸,通過(guò)調(diào)用sub_select函數(shù)來(lái)具體實(shí)現(xiàn)join功能入篮。

在上面的例子中,需要完成2個(gè)join:先join表A幌甘,再join表B(這里請(qǐng)注意潮售,不是涉及幾個(gè)表,就需要join幾個(gè)表锅风,MySQL的join優(yōu)化還是挺強(qiáng)大的酥诽,具體解釋見(jiàn)后)。在MySQL進(jìn)行sql解析時(shí)皱埠,會(huì)生成一個(gè)需要join的表的list肮帐,后面會(huì)挨個(gè)對(duì)該list的表進(jìn)行join操作。

繼續(xù)gdb边器,在sub_select函數(shù)中训枢,可以看到這樣一行代碼:(*join_tab->read_first_record)(join_tab)這個(gè)就是讀取表A的第一行結(jié)果,可以看join_tab里面的信息有表A的名字忘巧。接下來(lái)就是很關(guān)鍵的一個(gè)函數(shù):evaluate_join_record恒界,這個(gè)函數(shù)主要做2件事:

  1. 將當(dāng)前已經(jīng)拿到的信息進(jìn)行where條件計(jì)算,判斷是否需要繼續(xù)往下走砚嘴;
  2. 遞歸JOIN仗处;

還是以上面的sql為例眯勾,首先執(zhí)行第一個(gè)join,此時(shí)會(huì)遍歷表A的每一行結(jié)果婆誓,每遍歷一個(gè)結(jié)果吃环,會(huì)進(jìn)行where條件的判斷。這里需要注意:當(dāng)前的where條件判斷只會(huì)判斷已經(jīng)讀出來(lái)的列洋幻,由于此時(shí)只讀出來(lái)表A的數(shù)據(jù)郁轻,因此現(xiàn)在只能對(duì)第一個(gè)where條件,即A.age > 10進(jìn)行判斷文留,如果滿足好唯,則遞歸調(diào)用join:sql_select.cc: 11037 rc=(*join_tab->next_select)(join, join_tab+1, 0);,這里的next_select函數(shù)就是sub_select燥翅,MySQL就是這樣來(lái)實(shí)現(xiàn)遞歸操作的骑篙。如果不滿足,則不會(huì)遞歸join森书,而是繼續(xù)到下一行數(shù)據(jù)靶端,從而達(dá)到剪枝的目的。

繼續(xù)跟下去凛膏,此時(shí)通過(guò)上面的next_select遞歸的又調(diào)用到sub_select上杨名,同樣會(huì)走上面的邏輯,即先read_first_record猖毫,然后evaluate_join_record台谍,這里由于表A和表B的數(shù)據(jù)都有了,于是可以對(duì)上面后面2個(gè)where條件:A.id = B.idB.score > 60進(jìn)行判斷了吁断。到此趁蕊,所有的where條件都已經(jīng)判斷完畢,如果當(dāng)前行對(duì)3個(gè)where條件都滿足仔役,就可以將結(jié)果輸出掷伙。

以上就是select實(shí)現(xiàn)的大體過(guò)程,主要有2點(diǎn)骂因,一個(gè)是join是采用遞歸實(shí)現(xiàn)的,另一個(gè)是每讀一個(gè)表的數(shù)據(jù)赃泡,會(huì)將當(dāng)前的where條件進(jìn)行計(jì)算寒波,剪枝。還有一個(gè)細(xì)節(jié)沒(méi)有提到:MySQL是如何進(jìn)行where條件判斷的升熊?或者說(shuō)俄烁,MySQL是如何進(jìn)行表達(dá)式計(jì)算的?

答案就是前面提到的item類级野。當(dāng)MySQL在解析時(shí)页屠,會(huì)將sql解析為很多item,同時(shí)也會(huì)建立各個(gè)item之間的關(guān)系。對(duì)于表達(dá)式辰企,會(huì)生成一棵語(yǔ)法樹(shù)风纠。比如表達(dá)式:B.score > 60,此時(shí)會(huì)生成3個(gè)item:B.score牢贸、>60竹观,其中B.score60分別是>的左右孩子,這樣潜索,求表達(dá)式的值時(shí)臭增,就是求>val_int(),然后就會(huì)遞歸的調(diào)用左右子樹(shù)的val_int()竹习,再做比較判斷即可誊抛。

還有一個(gè)問(wèn)題:如何求B.scoreval_int()?對(duì)于此問(wèn)題的答案我沒(méi)有具體看過(guò)整陌,根據(jù)之前一個(gè)同事的sql實(shí)現(xiàn)方式拗窃,我是這樣推測(cè)的:B.score是數(shù)據(jù)表中的真實(shí)值,因此它的值肯定是通過(guò)去表中獲取蔓榄。在item類中并炮,有一個(gè)函數(shù):fix_field,它是用于告訴外界甥郑,去哪里獲取此item的值逃魄,往往在sql執(zhí)行的預(yù)處理階段調(diào)用。于是在預(yù)處理時(shí)澜搅,告訴該item去某個(gè)固定buffer讀取結(jié)果伍俘,同時(shí),每當(dāng)從表中讀出一行數(shù)據(jù)時(shí)勉躺,將該數(shù)據(jù)保存在該buffer中癌瘾,這樣就可以將兩者關(guān)聯(lián)起來(lái)。這個(gè)部分純屬個(gè)人推測(cè)饵溅,感興趣的同學(xué)可以自己根據(jù)源碼看看妨退。

再回到之前提到的一點(diǎn),如果我們將sql稍微改一下:select A.id, B.score from student A left join subject B on A.id=B.id where B.score > 60;蜕企,即去掉第一個(gè)where條件咬荷,此時(shí)會(huì)發(fā)生什么?

答案是轻掩,MySQL會(huì)做一個(gè)優(yōu)化幸乒,將sql轉(zhuǎn)換為select B.id, B.score from subject B where B.score > 60,這樣就不需要A同B join的邏輯了唇牧。實(shí)際上最開(kāi)始我在gdb時(shí)就用的這條sql罕扎,結(jié)果死活看不到遞歸調(diào)用sub_select的場(chǎng)景聚唐,還以為原理不對(duì),后來(lái)才發(fā)現(xiàn)是MySQL優(yōu)化搗的亂腔召。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杆查,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宴咧,更是在濱河造成了極大的恐慌根灯,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掺栅,死亡現(xiàn)場(chǎng)離奇詭異烙肺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)氧卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門桃笙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沙绝,你說(shuō)我怎么就攤上這事搏明。” “怎么了闪檬?”我有些...
    開(kāi)封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵星著,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我粗悯,道長(zhǎng)虚循,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任样傍,我火速辦了婚禮横缔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衫哥。我一直安慰自己茎刚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布撤逢。 她就那樣靜靜地躺著膛锭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚊荣。 梳的紋絲不亂的頭發(fā)上初狰,一...
    開(kāi)封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音妇押,去河邊找鬼跷究。 笑死姓迅,一個(gè)胖子當(dāng)著我的面吹牛敲霍,可吹牛的內(nèi)容都是我干的俊马。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肩杈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柴我!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扩然,我...
    開(kāi)封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤艘儒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后夫偶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體界睁,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年兵拢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翻斟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡说铃,死狀恐怖访惜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻扇,我是刑警寧澤债热,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站幼苛,受9級(jí)特大地震影響窒篱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚓峦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一舌剂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暑椰,春花似錦霍转、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至召夹,卻和暖如春岩喷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背监憎。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工纱意, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲸阔。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓偷霉,卻偏偏與公主長(zhǎng)得像迄委,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子类少,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 50個(gè)常用的sql語(yǔ)句Student(S#,Sname,Sage,Ssex) 學(xué)生表Course(C#,Cname...
    哈哈海閱讀 1,235評(píng)論 0 7
  • 什么是SQL數(shù)據(jù)庫(kù): SQL是Structured Query Language(結(jié)構(gòu)化查詢語(yǔ)言)的縮寫叙身。SQL是...
    西貝巴巴閱讀 1,822評(píng)論 0 10
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法硫狞,內(nèi)部類的語(yǔ)法信轿,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法残吩,線程的語(yǔ)...
    子非魚_t_閱讀 31,662評(píng)論 18 399
  • 花含羞财忽,月含羞, 紅燭芳樽醉西樓泣侮,游絲裊情柔定罢。 盟亦休,情亦休旁瘫, 落花流水去悠悠 祖凫,紅袖掩清愁。 花飄零酬凳,葉飄零惠况,...
    伊清歡閱讀 523評(píng)論 2 6
  • 項(xiàng)目要求根據(jù)服務(wù)器返回的視頻和秒數(shù),生成該視頻的預(yù)覽圖宁仔。網(wǎng)上一搜關(guān)鍵詞 “iOS 視頻 幀” 結(jié)果都是:iOS如何...
    TomatosX閱讀 20,459評(píng)論 10 44