字節(jié)二面你知道InnoDB行記錄存儲結(jié)構(gòu)嗎衰琐?

程序員常用的IDEA插件:https://github.com/silently9527/Toolkit

前言

我們平時在向MySQL數(shù)據(jù)庫表中插入數(shù)據(jù)時跑慕,實際數(shù)據(jù)是以行記錄的格式存儲在磁盤上的,本篇我們就一起來詳細的了解下MySQL的行記錄格式差油,理解了行記錄的格式有助于我們后面了解MySQL如何快速在頁中定位出行記錄估脆,以及MySQL的版本控制鏈,事務隔離級別等等衰倦,行記錄格式是許多MySQL核心知識的基礎(chǔ)袒炉。

InnoDB行記錄類型

MySQL中總共提供了四種類型的行格式:Compact,Redundant,Dynamic,Compressed

在創(chuàng)建表或修改表的時候可以指定行記錄的格式
create table 表名 row_format=行格式名
alter table 表名 row_format=行格式名

知道就行樊零,不需要去記住我磁,基本上使用不到

Compact行格式

在四種類型的行格式中孽文,我們主要來學習Compact格式,其他格式的行記錄類似夺艰;

image

從圖中我們可以看出行記錄主要是由4部分組成:變長字段長度芋哭、Null值列表,行記錄頭信息以及列的真實數(shù)據(jù)

變長字段長度列表

在MySQL中很一些變長的數(shù)據(jù)類型(varchar郁副,text等)减牺,MySQL需要知道這些數(shù)據(jù)的實際長度,這樣才能正確的在真實數(shù)據(jù)中取出對應列的數(shù)據(jù)存谎,所以變長字段是由兩部分組成:

  • 真實數(shù)據(jù)的長度
  • 真實數(shù)據(jù)的字節(jié)

每個變長字段的長度要么用1字節(jié)要么用2字節(jié)表示拔疚,由此就決定了每個字段的最大字節(jié)數(shù)是65535;

  • 假如字符類型若為gbk既荚,每個字符最多占2個字節(jié)稚失,最大長度不能超過32766;
  • 假如字符類型若為utf8,每個字符最多占3個字節(jié)固以,最大長度不能超過21845墩虹。

那到底什么時候選用1字節(jié)什么時候選用2字節(jié)呢?

這里需要定義三個變量:w,m,l

  1. 假如使用的字符集是utf8mb4憨琳,每個字符占用的字節(jié)數(shù)是4字節(jié)诫钓,那么w=4;假如字符類型若為utf8篙螟,每個字符最多占3個字節(jié)菌湃,那么w=3; 所以w表示字符集中每個字符所占的字節(jié)數(shù)
  2. varchr(m),這里m表示的是定義的字符的長度
  3. l 表示的是該字段真實數(shù)據(jù)占用的字節(jié)數(shù)

m*w <= 255遍略;表示該字段定義的最大長度都不會超過1字節(jié)惧所,那么該字段的長度就用1字節(jié)表示

m*w > 255 && l<=127; 表示該字段定義的長度可能會超過1個字節(jié),但是當前的實際長度是小于127的绪杏,可以用1個字節(jié)表示

m*w > 255 && l>127; 用2字節(jié)來表示該字段的長度

思考:為什么與l比較的值是127呢下愈?
當我們定義的變長字段可能大于255(也就是超過一個字節(jié))時,MySQL如何才能知道當前讀取的字節(jié)該字段的完成字段長度蕾久,還是該字段的半個字段長度势似,為了解決這個問題,MySQL使用了1字節(jié)的首位僧著,當首位為0表示當前是1字節(jié)履因,當首位為0表示當前長度是2字節(jié);由于占用了1字節(jié)的首位盹愚,所以剩下7位所能表示的最大值是127

變長字段不會存儲為Null列的長度栅迄;其次并不是行記錄中一定需要變長字段長度這段內(nèi)容,如果行記錄中沒有定義變長字段或者是變長字段都為Null皆怕,那么就不會有變長字段長度這部分

變長字段占用的字節(jié)數(shù)按照順序逆序存儲

Null值列表

一條記錄中某些列通骋阌撸可能允許為null西篓,所以Compact行格式把這些允許為null的進行了統(tǒng)一管理;

  1. 首先統(tǒng)計出表中定義的哪些列允許為null
  2. 如果表中的字段都不能為空憋活,那么就不存在null值列表污淋;如果存在允許為null的字段,那么就按照字段的順序為每個字段對應一個二進制位余掖,當二進制位為1時表示該列值為空寸爆;當二進制位位0時表示該列值不為空
  3. Null值列表必須有整數(shù)個字節(jié)來表示,所以對應沒有占用的位使用0補位
image

行記錄的頭信息

頭信息中主要包含了6個字段盐欺,其中5個字段也是在面試中經(jīng)常被問到的赁豆,為了方便記憶,我們把5個字段對應到手的5根指頭:

  • n_owned(拇指): 一個數(shù)據(jù)頁會被分成很多個組冗美,每組最后的一條記錄該字段為1魔种,其他記錄該字段為0,就像分組中所有的記錄的大哥粉洼;(對應拇指)

  • deleted_flag(食指): 標記該記錄是被刪除的节预;當記錄被刪除時不會真實刪除,而是用該字段標記属韧,并且把所有刪除的記錄使用鏈表連接起來安拟,以后的文章會繼續(xù)說到這個字段。(想象下你平時挖鼻屎是不是用的食指)

  • heap_no(中指): 表示當前記錄在數(shù)據(jù)頁中的相對位置(MySQL使用該字段來表示記錄位置宵喂,可以和中指對應糠赦,不可描述)

  • record_type(無名指): 表示當前記錄屬于哪種類型,(無名指用來帶戒指的锅棕,與分類有關(guān)拙泽,可以把人分為已婚和未婚,)

    1. 0表示普通記錄
    2. 1表示目錄項記錄裸燎,索引中非葉子結(jié)點中的數(shù)據(jù)記錄都是1
    3. 2表示infrmum記錄顾瞻,每個數(shù)據(jù)頁中至少會有兩條記錄,其中最小記錄的record_type=2
    4. 3表示Supremum記錄德绿,每個數(shù)據(jù)頁中至少會有兩條記錄荷荤,其中最大記錄的record_type=3
  • next_record(小拇指): 存放下一條記錄的相對位置(當數(shù)數(shù)時,左手的小拇指數(shù)完之后就該換右手了脆炎,和next_record表達的意思類型)

最后一個字段min_rec_flag : B+樹中每層非葉子結(jié)點最小目錄項記錄該字段為1梅猿;該字段相對于其他5個字段顯得不那么重要氓辣,不會影響理解B+樹索引

隱藏列

除了用戶自定義的數(shù)據(jù)列以外秒裕,MySQL還會為每行記錄生成3個隱藏列

  • row_id: 行ID,記錄的唯一標識钞啸;當用戶在表中定義了主鍵字段就優(yōu)先選擇用戶定義的主鍵几蜻,如果沒有喇潘,就查找是否有定義不為null的唯一索引,如果有就把該列作為主鍵梭稚,如果沒有MySQL就會生成一列row_id隱藏列作為主鍵
  • trx_id: 事務的ID颖低;該字段對于實現(xiàn)一致性視圖和事務隔離級別至關(guān)重要,以后會詳細說明
  • roll_pointer: 回滾指針弧烤,指向的是該記錄的上一個版本號忱屑,MySQL的MVCC主要就是通過這個字段來實現(xiàn)的。

溢出列

MySQL中所有的行記錄都會被存儲在數(shù)據(jù)頁中暇昂,每個數(shù)據(jù)頁的大小是16KB莺戒,也就是16384個字節(jié);在前面我們講過變長字段的長度可以用兩個字節(jié)來表示急波,所以列的最大長度可以是65535从铲,當遇到這種極端情況時,一個數(shù)據(jù)頁是存儲不下這一條記錄的澄暮。

Compact行格式針對這種情況的處理方式是在真實的數(shù)據(jù)處記錄該列的一部分數(shù)據(jù)(768字節(jié))名段,其他多余的數(shù)據(jù)會存儲到新的數(shù)據(jù)頁中(溢出頁),然后在該記錄中使用20個字節(jié)存儲這些數(shù)據(jù)頁的地址

image

溢出頁與溢出頁之間使用的鏈表相連接

其他的行記錄格式:

Redundant:MySQL5.0之前的格式泣懊,直接忽略

Dynamic,CompressedCompact很像伸辟,只是在溢出列的處理有些差異,他們只會在真實數(shù)據(jù)列中使用20個字節(jié)存儲溢出頁的地址

面試題

  • char(M)定義的字段馍刮,在變長字段的長度列表中會記錄該字段的長度嗎自娩?

歡迎大家在評論區(qū)留言討論


最后(點關(guān)注,不迷路)

文中或許會存在或多或少的不足渠退、錯誤之處忙迁,有建議或者意見也非常歡迎大家在評論交流。

最后碎乃,寫作不易姊扔,請不要白嫖我喲,希望朋友們可以點贊評論關(guān)注三連梅誓,因為這些就是我分享的全部動力來源??

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恰梢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梗掰,更是在濱河造成了極大的恐慌嵌言,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件及穗,死亡現(xiàn)場離奇詭異摧茴,居然都是意外死亡,警方通過查閱死者的電腦和手機埂陆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門苛白,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娃豹,“玉大人,你說我怎么就攤上這事购裙《妫” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵躏率,是天一觀的道長躯畴。 經(jīng)常有香客問我,道長薇芝,這世上最難降的妖魔是什么私股? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮恩掷,結(jié)果婚禮上倡鲸,老公的妹妹穿的比我還像新娘。我一直安慰自己黄娘,他們只是感情好峭状,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逼争,像睡著了一般优床。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上誓焦,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天胆敞,我揣著相機與錄音,去河邊找鬼杂伟。 笑死移层,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赫粥。 我是一名探鬼主播观话,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼越平!你這毒婦竟也來了频蛔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤秦叛,失蹤者是張志新(化名)和其女友劉穎晦溪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挣跋,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡三圆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫌术。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牌借,靈堂內(nèi)的尸體忽然破棺而出度气,到底是詐尸還是另有隱情,我是刑警寧澤膨报,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布磷籍,位于F島的核電站,受9級特大地震影響现柠,放射性物質(zhì)發(fā)生泄漏院领。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一够吩、第九天 我趴在偏房一處隱蔽的房頂上張望比然。 院中可真熱鬧,春花似錦周循、人聲如沸强法。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饮怯。三九已至,卻和暖如春嚎研,著一層夾襖步出監(jiān)牢的瞬間蓖墅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工临扮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留论矾,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓杆勇,卻偏偏與公主長得像拇囊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子靶橱,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

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