<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          《MySQL系列》01 InnoDB行記錄存儲(chǔ)結(jié)構(gòu)

          共 3028字,需瀏覽 7分鐘

           ·

          2021-08-23 02:37

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

          本文已被Github倉庫收錄 https://github.com/silently9527/ProgrammerNotes

          微信公眾號(hào):貝塔學(xué)Java

          前言

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

          InnoDB行記錄類型

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

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

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

          Compact行格式

          在四種類型的行格式中,我們主要來學(xué)習(xí)Compact格式,其他格式的行記錄類似;

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

          變長字段長度列表

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

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

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

          • 假如字符類型若為gbk,每個(gè)字符最多占2個(gè)字節(jié),最大長度不能超過32766;
          • 假如字符類型若為utf8,每個(gè)字符最多占3個(gè)字節(jié),最大長度不能超過21845。

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

          這里需要定義三個(gè)變量:w,m,l

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

          當(dāng) m*w <= 255;表示該字段定義的最大長度都不會(huì)超過1字節(jié),那么該字段的長度就用1字節(jié)表示

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

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

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

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

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

          Null值列表

          一條記錄中某些列通??赡茉试S為null,所以Compact行格式把這些允許為null的進(jìn)行了統(tǒng)一管理;

          1. 首先統(tǒng)計(jì)出表中定義的哪些列允許為null
          2. 如果表中的字段都不能為空,那么就不存在null值列表;如果存在允許為null的字段,那么就按照字段的順序?yàn)槊總€(gè)字段對(duì)應(yīng)一個(gè)二進(jìn)制位,當(dāng)二進(jìn)制位為1時(shí)表示該列值為空;當(dāng)二進(jìn)制位位0時(shí)表示該列值不為空
          3. Null值列表必須有整數(shù)個(gè)字節(jié)來表示,所以對(duì)應(yīng)沒有占用的位使用0補(bǔ)位

          行記錄的頭信息

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

          • n_owned(拇指): 一個(gè)數(shù)據(jù)頁會(huì)被分成很多個(gè)組,每組最后的一條記錄該字段為1,其他記錄該字段為0,就像分組中所有的記錄的大哥;(對(duì)應(yīng)拇指)

          • deleted_flag(食指): 標(biāo)記該記錄是被刪除的;當(dāng)記錄被刪除時(shí)不會(huì)真實(shí)刪除,而是用該字段標(biāo)記,并且把所有刪除的記錄使用鏈表連接起來,以后的文章會(huì)繼續(xù)說到這個(gè)字段。(想象下你平時(shí)挖鼻屎是不是用的食指)

          • heap_no(中指): 表示當(dāng)前記錄在數(shù)據(jù)頁中的相對(duì)位置(MySQL使用該字段來表示記錄位置,可以和中指對(duì)應(yīng),不可描述)

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

            1. 0表示普通記錄
            2. 1表示目錄項(xiàng)記錄,索引中非葉子結(jié)點(diǎn)中的數(shù)據(jù)記錄都是1
            3. 2表示infrmum記錄,每個(gè)數(shù)據(jù)頁中至少會(huì)有兩條記錄,其中最小記錄的record_type=2
            4. 3表示Supremum記錄,每個(gè)數(shù)據(jù)頁中至少會(huì)有兩條記錄,其中最大記錄的record_type=3
          • next_record(小拇指): 存放下一條記錄的相對(duì)位置(當(dāng)數(shù)數(shù)時(shí),左手的小拇指數(shù)完之后就該換右手了,和next_record表達(dá)的意思類型)

          最后一個(gè)字段min_rec_flag : B+樹中每層非葉子結(jié)點(diǎn)最小目錄項(xiàng)記錄該字段為1;該字段相對(duì)于其他5個(gè)字段顯得不那么重要,不會(huì)影響理解B+樹索引

          隱藏列

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

          • row_id: 行ID,記錄的唯一標(biāo)識(shí);當(dāng)用戶在表中定義了主鍵字段就優(yōu)先選擇用戶定義的主鍵,如果沒有,就查找是否有定義不為null的唯一索引,如果有就把該列作為主鍵,如果沒有MySQL就會(huì)生成一列row_id隱藏列作為主鍵
          • trx_id: 事務(wù)的ID;該字段對(duì)于實(shí)現(xiàn)一致性視圖和事務(wù)隔離級(jí)別至關(guān)重要,以后會(huì)詳細(xì)說明
          • roll_pointer: 回滾指針,指向的是該記錄的上一個(gè)版本號(hào),MySQL的MVCC主要就是通過這個(gè)字段來實(shí)現(xiàn)的。

          溢出列

          MySQL中所有的行記錄都會(huì)被存儲(chǔ)在數(shù)據(jù)頁中,每個(gè)數(shù)據(jù)頁的大小是16KB,也就是16384個(gè)字節(jié);在前面我們講過變長字段的長度可以用兩個(gè)字節(jié)來表示,所以列的最大長度可以是65535,當(dāng)遇到這種極端情況時(shí),一個(gè)數(shù)據(jù)頁是存儲(chǔ)不下這一條記錄的。

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

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

          其他的行記錄格式:

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

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

          面試題

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

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







          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  卡一卡二无码免费在线 | 91午夜福利视频 | 三级精品无码 | 热久久9| 人人摸人人操人人 |