1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        阿里面試被問:MySql數(shù)據(jù)是如何存儲在磁盤上存儲的?

        共 5957字,需瀏覽 12分鐘

         ·

        2021-06-14 12:55


        關(guān)于MySql數(shù)據(jù)庫,相信很多人都不陌生,這是當今最常用的一種關(guān)系型數(shù)據(jù)庫,關(guān)于MySql的知識也是很豐富的。
        那么,不知道大家有沒有想過這樣的問題:MySql中的數(shù)據(jù)是存在哪的?又是如何存儲的呢?
        本文就來深入分析一下這些問題。文章內(nèi)容很長,建議收藏,建議大家靜下心來仔細閱讀,一定會有收獲!



        Innodb的存儲格式
        我們知道,關(guān)于Mysql這種關(guān)系型數(shù)據(jù)庫,里面保存的數(shù)據(jù)最終都是要持久化到磁盤文件上面的。磁盤文件里存放的物理格式就是數(shù)據(jù)頁(關(guān)于數(shù)據(jù)頁,如果不太理解先忽略,后續(xù)文章單獨介紹),數(shù)據(jù)頁中存放的是一行一行的記錄,但是對于數(shù)據(jù)頁中的每一行數(shù)據(jù)他又是怎么存儲的呢?
        我們拿Mysql中最常用的Innodb引擎來重點說,介紹下存儲格式是怎樣的。
        MySQL中存儲有3種:
        1. server層格式:與存儲引擎無關(guān),Binlog存儲常用的一種 (Bin Log 我們前面已經(jīng)詳細介紹過了,這個是MySql主從復(fù)制的一個很重要的文件)
        2. 索引元組格式:InnoDB存取過程記錄的中間狀態(tài),是InnoDB在內(nèi)存中存儲的格式 (換句話說我們的增刪改的操作都是在內(nèi)存中執(zhí)行的,這個只是一種臨時狀態(tài))
        3. 物理存儲格式:記錄在物理頁面中的存儲格式,即compact格式,與索引元組格式一一對應(yīng)。(這個是數(shù)據(jù)在磁盤存儲的真正的格式)
        MySql 的 InnoDB 存儲引擎和大多數(shù)數(shù)據(jù)庫一樣,都是以行的形式存儲數(shù)據(jù)的,我們可以通過SHOW TABLE STATUS查看到行的的存儲格式。
        InnoDB 儲存引擎支持有四種行儲存格式:COMPACT、Redundant、Dynamic 和 COMPRESSED。默認為COMPACT。
        其他的參數(shù)我們這里不關(guān)注,僅僅看 Row_format 這列,這里我們可以看到行的存儲格式是 Compact,Compact 存儲數(shù)據(jù)的格式大致如下這樣
        對于我們看到的每一行數(shù)據(jù),我們最先看到的好像并不是各個列,而是一些類似列的描述信息。沒錯,其實在存儲的時候都會有一些都字段來描述這一行的信息,這就好比緩存池中的描述緩存頁的描述數(shù)據(jù)類似。
        上面的圖片大家可以這么簡化來對待,事務(wù)ID回滾指針大家先不要關(guān)注,免得因為這個產(chǎn)生干擾而難于理解



        變長字段 varchar 是如何存儲的
        一般情況下,我們要存儲的數(shù)據(jù)是并不能確定他的長度的,大部分情況下都是一些變長的數(shù)據(jù),以varchar為例,假設(shè)現(xiàn)在三個字段,字段類型分別為:varchar(10),char(1),char(1),char大家都是知道的,存儲的基本是一些已知的長度固定的數(shù)據(jù),假設(shè)這三個類型的字段分別有如下的數(shù)據(jù):
        第一行:mysql a a;第二行:dog b c;畫個圖來幫助大家想象,現(xiàn)在你看到的是數(shù)據(jù)中為我們展現(xiàn)的樣子。
        但是在磁盤中可不是這樣子的,前文已經(jīng)提到過,表空間和行這些其實是邏輯上的概念,而數(shù)據(jù)頁是一種物理概念,也就是說我們看到的樣子在磁盤中的樣子本本是不一樣的。
        在磁盤中這兩條記錄大致是這樣子的:mysql a a dog b c,他們在磁盤中都是挨在一起存儲的。
        是不是瞬間感覺想要去查找一條數(shù)據(jù)非常麻煩,告訴你:是的,所以 MySql在設(shè)計的時候才會使用行格式存儲,才會有前面的哪些變長字段列表和標志位以及記錄信息,這些就是用來記錄一行的記錄的信息,換句話說,MySql是通過這些描述信息來定位到一行中的具體記錄的。
        以第一行記錄為例,它在磁盤中的記錄情況大致是下面這樣子的,首先我們需要明確知道的是各個字段的類型MySql是很清楚的,在這個基礎(chǔ)上我們能看明白下面和想通后面的事情。首先我們看到 mysql是5個字符,使用十六進制表示是 0x05,所以他的存儲大概是這樣子的:
        同理第二行數(shù)據(jù)類似這樣子的:
        相信大家在看到這里已經(jīng)大概能推測出MySql這個時候是怎么讀讀取數(shù)據(jù)的了,就是他會先根據(jù)變長字段長度列表中描述的變長字段的信息去查找變長字段,例如第一行,MySql解析到變長字段是5,所以他會在mysql a a dog b c 這些里面取出5個字符,也就是 mysql,緊接著后面是兩個 char(1) 也就是兩個 a 在依次取出來。
        中間設(shè)備。由淺入深,我們慢慢來,剛剛上面說到的僅僅是一種非常簡單的情況,這個首先是幫助大家理解,讓大家先明白有這么個回事,是這么回事,然后在慢慢的挖掘,我們一定要一個蘿卜一個坑的去踏實學(xué)習(xí)
        現(xiàn)在如果是多個varchar類型的字段怎么辦?例如:varchar(3),varchar(10),varchar(4),char(1),他有一條記錄是這樣子的:aaa ,bb,cccc,d,你根據(jù)上面的能推測出磁盤中的行記錄是怎么樣子的嗎?
        你是不是這么想的:磁盤中肯定是這樣的:0x03,0x02,0x04 null標志位 記錄頭信息 aaa bb cccc d;這么想的同學(xué)請鼻子靠墻:);實際上并不是這樣子的。
        當有多個變長字段的時候,MySql在 compact 行格式中,把所有變長類型的長度存放在行記錄的開頭部位形成一個列表(這個列表就是剛剛上面說的變長字段列表),按照列的逆序存放,也就是大致是這樣子的:
        這里我必須要給大家解釋下變長字段列表會逆序存放,因為每行記錄的都有一個 next_record指針 指向下一行 記錄頭信息真實數(shù)據(jù) 之間的位置。因為這個位置剛剛好,向左讀取就是行描述相關(guān)信息,向右讀取就是真實數(shù)據(jù)。正好對應(yīng)變長字段長度列表。畫個圖來幫助大家理解下:
        說到這里我們來稍微小結(jié)一下



        MySql中數(shù)據(jù)在磁盤的存儲小結(jié)
        1. 數(shù)據(jù)在磁盤中的存儲在物理空間上面是連續(xù)的
        2. 數(shù)據(jù)是被存放在MySql設(shè)計出來的數(shù)據(jù)頁上面的,數(shù)據(jù)頁上面存儲的才是最終的一行一行的記錄
        3. 行的存儲格式默認是Compact
        4. 每一行數(shù)據(jù)都會有相應(yīng)的行描述部分,描述部分有【變長字段列表】【NULL標志位】【記錄頭信息】
        5. 每一行都會有next_record指針,指向記錄頭和變長字段列表的中間某個位置,方便尋址
        6. 變長列表中的varchar列的描述是逆序的(和字段的順序相反)這樣做的目的在上圖中描述的很清楚了



        NULL字段是如何存儲的
        上面說到了情況都是比較正常的情況,也就說上面提到的字段是沒有空值的,不管是變長字段還是char字段,都是有值的,那如果某個字段允許為空,且值確實為空,MySql又是怎么處理的呢?是不是直接存儲NULL呢。
        假設(shè)MySql針對與Null直接存儲,他實際上是按照“NULL”這樣字符串的形式存儲的,這樣顯然不行啊,因為字符串要占用空間的啊(一個 NULL 字符串要占用四個字符呢),你都沒有值,還占這么多空間,所以MySql肯定不是這樣存儲的。其實MySql在處理NULL值的時候是會將它通二進制來存儲的,且也是逆序的



        MySql是如何通過二進制來存儲NULL值的?
        上面的 Compact 格式數(shù)據(jù)中的【NULL標志位(也可以叫NULL列表)】就是用來存儲NULL值的。若有某個字段值為 null,將將其 bit 位置為 1 說明值為 NULL,bit為 0 說明該字段值不為空
        是不是聽了解釋還是稀里糊涂的,別急,我畫個圖再來詳細介紹下,先假設(shè)我們有一張 sutdents 表

        CREATE TABLE `students` (

          `name` varchar(10NOT NULL,

          `address` varchar(255DEFAULT NULL,

          `gender` char(1DEFAULT NULL,

          `class` varchar(10DEFAULT NULL,

          `hobbies` varchar(255DEFAULT NULL,

          PRIMARY KEY (`name`)

        他有這樣一行記錄
        我們先看變長字段列表部分(記住是逆序存放的):
        roles是長度為5記作:0x05;address 為null,不放在變長列表中、gender 是 char 類型,不放在變長列表中、class為空,不放在變長列表中、hobby_xx長度為8記作:0x08;所以變長列表的記錄為:0x08 0x05
        現(xiàn)在到了NULL標志位了:依舊是從右往左記錄字段:name 在設(shè)計的時候就是 not null,所示是不會出現(xiàn)在NULL標志為中(Null標志為是用來記錄字段可為NULL的字段,字段不可以為NULL的不是會被記錄到NULL標志位的),address為NULL記作1,gender不為null記作0,class 為null記作1,hobbies不為null記作0;所以按照字段的順序結(jié)果就是:0101,但是NULL標志位是逆序的,所以NULL標志位存放的結(jié)果大概是這樣子的:0101,高位補0即可
        我們來模擬讀取下這條記錄:MySql 對于字段的類型一定是已知的(這個是在創(chuàng)建數(shù)據(jù)表的時候就已經(jīng)定下來了),所以對于 name 這種 not nul l的字段是不會去存放在null標志位的,下面是詳細的讀取步驟:
        • name字段是主鍵,不可能在NULL 標志位中的,又因為 name 是varchar 字段,所以就會去變長字段列中查找,找到值為 0x05 接著就會去字段列表中讀取5個字符的長度,也就是 roles ,第一個字段讀取成功;
        • 接著是 address 字段,因為類型是 MySql 已知的,又因為字段值為 null 所以就不需要去讀取了,第二個字段讀取結(jié)束;
        • 接著是gender字段,是char類型的,直接拿到 f 就可以了;
        • 下一個是class 字段,因為是null 所以根本不會去變長字段中查找;
        • 最后一個是 hobbies 字段,因為不為null ,又是第二個變長字段,這個時候就會去 變長字段列表中查找,結(jié)果定位到是 0x08 那就讀取 8 個字符的長度出來,拿出來是hobby_xx;
        說到這里,關(guān)于一行記錄的中的變長字段列表和 NULL 標志位具體是如何讀取字段值的就給大家介紹完了,不知道大家看到以上內(nèi)容腦子是不是會展現(xiàn)一條條行記錄的描述信息。目前我們只需要了解 varchar 和 NULL 存儲的基本就足夠了,因為這兩個表特殊,也是最經(jīng)常使用的,其他的字段類型本篇暫且不展開討論了。
        上面的記錄頭的信息我們還沒有討論過,下面我們再詳細介紹下記錄頭信息是什么。



        記錄頭信息
        記錄頭信息由40位的bit位組成,其各個位的劃分和含義如下:

        記錄頭的各個位的作用其實就已經(jīng)說的很清楚了,一些概念現(xiàn)在還沒法講解,很多東西需要到索引的時候才能展開講,這里大家需要明確的就是各個標志位的含義。
        我認為對于記錄頭的了解到這里就足夠了,各個標志位的含義明確了到這個程度就行了,至于更多的可能我們根本接觸不到。這一小節(jié)就當是科普。



        數(shù)據(jù)在磁盤上到底是怎么存儲的
        上面畫過這樣一張圖:
        之前說的是數(shù)據(jù)大致是這樣子在磁盤中存儲的:0x03 NULL標志位 記錄頭信息 dog b c,但是實際上后面的列的數(shù)據(jù)并是不是我們看到的這個樣子,磁盤在存儲的時候是根據(jù)數(shù)據(jù)庫指定的字符集編碼存儲起來的你以為可能是上面那樣子存儲的。
        實際上可能是在樣子的:0x03 NULL 標志位 記錄頭信息 1233 323 223,也就是說實際的數(shù)據(jù)在磁盤上存儲根本不是我們?nèi)四苷J識的,后面的 1233 323 223 這幾個是我亂寫的,沒什么含義,主要是想表明是計算在實際存儲的時候是以特定的字符編碼來存儲的。
        另外每一行數(shù)據(jù)在被存儲的時候?qū)嶋H上還會有隱藏的字段,相信大家對這個應(yīng)該不會陌生的,row_id 大家應(yīng)該是知道的,哪怕自己沒用過可能也是聽過的,這個是數(shù)據(jù)庫自己為我們的每一行記錄生成的一個唯一的表示,如果我們沒有為數(shù)據(jù)表指定主鍵字段,也沒有指定 Unique key,那么這個時候數(shù)據(jù)庫內(nèi)部會幫我們維護一個自增長的 ROW_ID 字段作為主鍵。
        還有一個隱藏字段就是 事務(wù)ID 上面的第二張圖上層畫出來過,這個顧名思義了,就是和事務(wù)相關(guān)的一個字段屬性字段名為DB_TRX_ID,這個再詳解到事務(wù)的時候再詳細介紹;最后一個也是在上面的第二張圖上畫出來了,就是回滾指針 DB_ROLL_PTR,回滾也是事務(wù)使用到的概念,也是放在事務(wù)那邊跟大家介紹
        現(xiàn)在再來整體回顧下一行記錄在磁盤中的存儲的結(jié)構(gòu)大概是什么樣子的:
        0x08 0x05 00000101 0000010100000000000000000000000000000010 21134 44 232343
        說到了存儲,我們順便聊聊和存儲相關(guān)的一個概念,行溢出。



        行溢出
        說到這里,不知道大家有沒有想過一個問題,就是我們一直在說 MySql 存儲是以數(shù)據(jù)頁的形式來存儲的,然后數(shù)據(jù)頁中記錄的是一行行的記錄,但是往往常規(guī)情況下不會有什么問題。
        但是如果現(xiàn)在有一行記錄非常大,因為數(shù)據(jù)頁大小默認也就是16KB,假設(shè)某張表里面有text字段也有BLOB字段,且這一行的記錄的大小遠遠超過了一個數(shù)據(jù)頁的大小16KB,這種情況稱之為行溢出。
        MySql 是怎么來處理這種行溢出的情況的呢?實際上很簡單,一個數(shù)據(jù)頁不夠就使用多個數(shù)據(jù)頁,數(shù)據(jù)頁和數(shù)據(jù)頁之間使用鏈表連起來,之所以能夠使用鏈表連接因為數(shù)據(jù)頁里面是包含了存放指針的 bit 位。對于行溢出的概念了解到這個程度就足夠了。我們學(xué)習(xí)是有的放矢,不是什么都要去刨根問底的。



        結(jié)束語
        本片文章詳細的介紹了 MySql 存儲數(shù)據(jù)的格式和數(shù)據(jù)具體在磁盤中是怎么存儲的,被存儲的數(shù)據(jù)又是怎么查找的,說白了很多事情都是已經(jīng)是既定的規(guī)則,所謂既定的規(guī)則就是很對東西已經(jīng)被更早的設(shè)計出來。

        所以你在使用和了解的使用只需要按照被人的規(guī)則來執(zhí)行,然后在此基礎(chǔ)上深入了解下別人為什么這么設(shè)計?這樣會更有助于我們掌握和理解某個知識點。

        關(guān)注公眾號【Java技術(shù)江湖】后回復(fù)“PDF”即可領(lǐng)取200+頁的《Java工程師面試指南》

        強烈推薦,幾乎涵蓋所有Java工程師必知必會的知識點,不管是復(fù)習(xí)還是面試,都很實用。



        瀏覽 41
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            老师洗澡一级毛片 | av91探花 | 黑人靠逼视频 | 肉丝草逼视频 | 老妇配另类HD | 性爱视频播放 | 性感一区二区三区 | 我慢慢进入岳的两片间 | 成人网站视频在线观看 | 亚洲天天干 |