<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數(shù)據(jù)庫(kù)一行數(shù)據(jù)在磁盤上是怎么存儲(chǔ)的?

          共 2847字,需瀏覽 6分鐘

           ·

          2021-10-17 09:11

          數(shù)據(jù)庫(kù)給使用者最直觀的感覺(jué),就是庫(kù)、表、行、字段,這些概念都是邏輯上的。前面我們深入講解了Buffer Pool的內(nèi)部原理,它的基本存儲(chǔ)單位是默認(rèn)大小為16K的頁(yè)。每頁(yè)都保存了一行一行的數(shù)據(jù)。我們按照數(shù)據(jù)頁(yè)為單位把磁盤上的數(shù)據(jù)加載到內(nèi)存的緩存頁(yè)里來(lái),也是按照頁(yè)為單位,把緩存頁(yè)的數(shù)據(jù)刷入磁盤的數(shù)據(jù)頁(yè)中。


          而我們常常聽(tīng)到數(shù)據(jù)頁(yè)、數(shù)據(jù)區(qū)、表空間這些名詞,其實(shí)這些名詞是物理層面上的概念。我們不經(jīng)要問(wèn),庫(kù)、表、行、字段,這些邏輯上的概念是如何對(duì)應(yīng)到物理層的概念上的呢?我們查詢一行數(shù)據(jù),是如何找到條數(shù)據(jù)所在的數(shù)據(jù)頁(yè)的呢?


          接下來(lái),筆者用幾篇文章,講解下MySQL的表空間、數(shù)據(jù)區(qū)、數(shù)據(jù)頁(yè)這些概念,當(dāng)大家明白搞明白這些東西后,就自然理解上面的問(wèn)題了。


          行格式


          行格式即行記錄的物理存儲(chǔ)格式,決定了這張表數(shù)據(jù)的物理存儲(chǔ)方式,會(huì)影響crud性能。


          指定行格式

          CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名稱;ALTER?TABLE?表名?ROW_FORMAT=行格式名稱;


          InnoDB 包含以下四種行格式

          • Compact
          • Redundant
          • Dynamic
          • Compressed



          mysql5.7之前的版本使用的是compact行格式,5.7及以后的版本使用的是dynamic行格式。Compact和Dynamic應(yīng)用較廣泛,compact是目前使用最多的一種,而dynamic是新版本默認(rèn)的行記錄格式。

          初識(shí)MySQL行數(shù)據(jù)存儲(chǔ)格式

          我們這里就以Compact存儲(chǔ)格式來(lái)講解。

          Compact行格式下,每行數(shù)據(jù)的存儲(chǔ)格式,大概是這樣的:

          變長(zhǎng)字段(記錄的長(zhǎng)度)列表,null值列表,數(shù)據(jù)頭,col1的值,col2的值,col3的值......

          Compact 中一條完整的記錄可以被分成'記錄的額外信息'和'記錄的真實(shí)數(shù)據(jù)' 兩部分,其他三種存儲(chǔ)存儲(chǔ)格式基本大同小異。

          變長(zhǎng)字段是如何存儲(chǔ)的?

          MySQL中變長(zhǎng)字段長(zhǎng)度是不固定的,?比如VARCHAR(50),實(shí)際存儲(chǔ)的內(nèi)容可能是"hello",也可能是“hello world"。

          所以,我們要讀取這類數(shù)據(jù)字段,必須要知道它的長(zhǎng)度。

          比如一行數(shù)據(jù),它的幾個(gè)字段為VARCHAR(10), VARCHAR(5)
          ,VARCHAR(20),CHAR(1),CHAR(1),插入一行數(shù)據(jù):hello, ni, hao, a, b。

          此時(shí)磁盤中存儲(chǔ)的行開(kāi)頭的變長(zhǎng)字段長(zhǎng)度列表,必須存儲(chǔ)幾個(gè)變長(zhǎng)字段的長(zhǎng)度,需要注意的是,這里是逆序存儲(chǔ)的。

          行數(shù)據(jù)存儲(chǔ)格式是這樣的:

          0x03 0x02 0x05 null值列表 頭字段?hello, ni, hao,?a, b

          NULL值列表

          null值列表,說(shuō)的就是一行數(shù)據(jù)里可能有的字段是選填的,值可以是null。比如name字段,如果允許為null,那么實(shí)際存儲(chǔ)的時(shí)候,要標(biāo)記出來(lái)。

          為了節(jié)省存儲(chǔ)空間,MySQL設(shè)計(jì)的時(shí)候,使用二進(jìn)制的bit位來(lái)存儲(chǔ)null值列表。

          假如創(chuàng)建了一張表:
          CREATE TABLE user (name VARCHAR(10)NOT NULL,    address VARCHAR(20),    gender CHAR(1),    job VARCHAR(30),    school VARCHAR(50))ROW_FORMAT = COMPACT;
          user表5個(gè)字段,4個(gè)變長(zhǎng)的,只有name是非NULL的,其他的4個(gè)字段都是可以為NULL的。


          假如現(xiàn)在插入一行數(shù)據(jù):zhangsan NULL M NULL Tsinghua,address和job是NULL,那么它在磁盤上是怎么存儲(chǔ)的?

          NULL值列表,是這樣存儲(chǔ)的,你所有允許值為NULL的字段,都會(huì)有一個(gè)二進(jìn)制的bit值,bit值為1說(shuō)明是NULL,如果bit值為0說(shuō)明不是NULL。

          上面插入的那行數(shù)據(jù),addressgender、job、school4個(gè)字段都允許為NULL,每個(gè)字段都會(huì)有一個(gè)bit位,其中address與job是NULL,所有4個(gè)bit位應(yīng)該是:1010。

          而且NULL值列表也是逆序存儲(chǔ)的,所以NULL值列表里是0101。

          一般NULL值列表是8個(gè)bit位的倍數(shù),如果不足8個(gè)bit位,則高位補(bǔ)0。

          所以現(xiàn)在看來(lái),行數(shù)據(jù)存儲(chǔ)格式是這樣的:

          0x08 0x08?00000101 頭信息?col1的值,col2的值,col3的值......

          數(shù)據(jù)頭以及真實(shí)數(shù)據(jù)

          行數(shù)據(jù)的頭長(zhǎng)度是固定的40bit,第一個(gè)bit和第二個(gè)bit,都是預(yù)留的,暫時(shí)沒(méi)有用到。

          記錄頭信息詳細(xì)如下表所示,有的暫時(shí)沒(méi)有用到,可以暫時(shí)不用深究它。


          現(xiàn)在再加上數(shù)據(jù)頭部分,上面的行存儲(chǔ)格式就變成這樣了,
          0x08 0x08?00000101 0000000000000000000010000000000000011001 zhangsan M Tsinghua

          剛開(kāi)始先是變長(zhǎng)字段的長(zhǎng)度,用16進(jìn)制表示,然后是NULL值列表,標(biāo)識(shí)哪些值是NULL,接著是40bit的數(shù)據(jù)頭,最后是真實(shí)數(shù)據(jù)。

          在讀取數(shù)據(jù)的時(shí)候,也會(huì)根據(jù)變長(zhǎng)字段的長(zhǎng)度,先讀取出長(zhǎng)度為8的zhangsan。

          然后發(fā)現(xiàn)第二個(gè)字段是NULL,就不用再讀了。

          第三個(gè)字段是定長(zhǎng)的1,就直接讀取出gender為M。

          第四個(gè)字段是NULL,就不用再讀了。

          第五個(gè)字段變長(zhǎng),長(zhǎng)度是8,再讀取長(zhǎng)度為8的Tsinghua。

          然而,真正磁盤上存儲(chǔ)的時(shí)候,那些字符串就是直接存儲(chǔ)在磁盤上的嗎?

          實(shí)際上,字符串都是根據(jù)數(shù)據(jù)庫(kù)指定的字符集編碼,進(jìn)行編碼之后再存儲(chǔ)的,編碼后大概是這樣的:

          0x08 0x08?00000101?0000000000000000000010000000000000011001?341324?134546 9342345

          大家會(huì)看到上面,字符串和其他類型的數(shù)值最終都會(huì)根據(jù)數(shù)據(jù)庫(kù)字符集編碼,變成一些數(shù)字和符號(hào)存儲(chǔ)在磁盤上。

          最后,在實(shí)際存儲(chǔ)一行數(shù)據(jù)的時(shí)候,MySQL還會(huì)給每條記錄,加入一些隱藏字段。如下表:


          如果用戶沒(méi)有指定主鍵,且表中沒(méi)有Unique鍵時(shí)才會(huì)使用DB_ROW_ID作為主鍵。

          最終,上面那條數(shù)據(jù)的存儲(chǔ)格式,變成這樣了:
          0x08 0x08?00000101?0000000000000000000010000000000000011001?00000000034C(DB_ROW_ID)00000000036D(DB_TRX_ID) EA000010022B(DB_ROL_PTR) 341324?134546 9342345

          到這里為止,我們基本把一行數(shù)據(jù)在磁盤上是如何存儲(chǔ)的講清楚了。

          有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 58
          點(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青草视频 | 日本免费一级片 | 老司机亚洲 | 爱操成人网 |