資料是如何儲存在磁碟的

2022-06-28 21:09:09 字數 4098 閱讀 7631

我們腦子裡理解資料的儲存就是『乙個庫裡面有一些表,表裡面有很多字段,然後有很多行資料』,但其實這只是我們的邏輯概念,資料在磁碟物理儲存方式可不是這樣的。mysql在伺服器上是以乙個資料夾的形式出現的,你建立了test資料庫,那麼在磁碟上就會出現test的目錄,而建立的那些表,其實都是有乙個表空間的概念,在磁碟都會對應乙個表名.ibd資料檔案。

表空間的磁碟檔案裡有很多個資料頁,為了管理,表空間裡又引入了乙個資料區(extent)的概念。每個資料區大小是1mb,乙個資料頁只有16kb、所以資料區對應著64個連續資料頁,然後256個資料區被劃分為一組。對於表空間而言,他的每個資料組的第乙個資料區的前3個資料頁,都是固定的,存放表空間和資料頁相關描述資訊的其他資料區的前兩個資料頁,也是放一些相關屬性描述的。

在研究 buffer pool 的時候我們知道是以資料頁為單位載入資料的,同樣也是以頁為單位將資料刷盤到磁碟的。 那這個資料頁到底長什麼樣子呢?它其實拆分成了很多個部分,大體來說包含了檔案頭、資料頁頭、最小記錄和最大記錄、多個資料行、空閒空間、資料頁目錄、檔案尾部。

檔案頭佔據了38個位元組,資料頁頭佔據了56個位元組,最大記錄和最小記錄佔據了26個位元組,資料行區域的大小是不固定的,空閒區域的大小也是不固定的,資料頁目錄的大小也是不固定的,然後檔案尾部佔據8個位元組。

對於資料頁中的每一行資料儲存,這裡涉及到"行格式"的概念,就是我們可以對乙個表指定它的行儲存的格式是什麼樣,既可以在建表時候指定、也可以後續修改儲存格式,這裡我以compact為例,除了這個還有其他集中儲存的格式,基本都大同小異。那比如我現在這裡有一張表,五個字段分別為name、address、genderjob、school,就代表了客戶的姓名、位址、性別、工作以及學校。

create

table

customer (

name

varchar(10) not

null

, address

varchar(20

), gender

char(1

), job

varchar(30

), school

varchar(50

)) row_format

=compact;

compact的儲存格式大概分為4部分 變長字段的長度列表,null值列表,資料頭,具體每一列資料。現在有這麼一行資料「jack null m null xx_school」,它的儲存格式大概長下面這樣,接下來我會逐步分析每個欄位的具體含義。

0x09 0x04 00000101 0000000000000000000010000000000000011001 616161 636320 6262626262

變長字段列表

mysql中有些字段長度是固定的,有些長度是不固定的,比如varchar這種不固定的,就叫變長字段。比如現在表裡的五個字段值為 hello hi b word a 那麼此時你要讀取第乙個欄位的值,那麼第乙個欄位是變長的,到底他的實際長度是多少呢?此時你會發現第一行資料的開頭有乙個變長字段的長度列表,裡面會讀取到乙個0x05這個十六進製制的數字,發現第乙個變長字段的長度是5,於是按照長度為5,讀取出來第乙個欄位的值,就是「hello」,同樣的後面分別為 0x02 0x04 0x01 就取出了 hi word a ,至於長度固定了的char(1)就直接取出來了 b 。只是變長字段列表是逆序儲存的!也就是 0x02 0x04 0x01 null值列表 資料頭  hello hi b word a。

null值列表

null值列表,顧名思義,說的就是你一行資料裡可能有的字段值是null,比如你有乙個name欄位且允許為null的,那麼實際上在儲存的時候,如果你沒給他賦值,他這個欄位的值就是null。比如現在表中一行記錄為「jack null m null xx_school」,他的5個字段裡有兩個欄位都是null。我們知道對於變長字段會記錄在變長字段列表裡面,但是這裡要區分一下,那就是如果這個變長字段的值是null,就不用在變長字段長度列表裡 存放他的值長度了,所以在上面那行資料中,只有name和school兩個變長字段是有值的,把他們的長度按照逆序放在變長字段長度列表中就可以了,如下所示:0x09 0x04 null值列表 頭資訊 column1=value1 column2=value2 ... columnn=valuen

null值在磁碟上,是通過二進位制的bit位來儲存的,如果bit值是1說明是null,如果bit值是0說明不是null。比如上面4個字段都允許為null,每個人都會有乙個bit位,這一行資料的值是「jack null m null xx_school」,所以4個bit位應該是:1010;但實際他是按逆序放的,所以是:0101。而null值列表一般起碼是8個bit位的倍數,如果不足8個bit位就高位補0,所以實際存放看起來是如下的:0x09 0x04 00000101 資料頭 column1=value1 column2=value2 ... columnn=valuen

資料頭資料頭是用來描述這行資料的,它只有40個bit位大小。

前兩個bit位都是預留位,沒有任何含義

第三個是delete_mask,用來標記這行資料是否被刪除了

第四個是min_rec_mask,他其實就是說在b+樹里每一層的非葉子節點裡的最小值都有這個標記

接下來有4個bit位是n_owned,他其實就是記錄了乙個記錄數

接著有13個bit位是heap_no,他代表的是當前這行資料在記錄堆裡的位置

然後是3個bit位的record_type,這就是說這行資料的型別:0代表的是普通型別,1代表的是b+樹非葉子節點,2代表的是最小值資料,3代表的是最大值資料

最後是16個bit的next_record,這個是指向他下一條資料的指標

那麼他真實儲存大致如下所示:

資料值我們已經知道他的變長字段的長度,用十六進製制來儲存,然後是null值列表,指出了誰是null,接著是40個bit位的資料頭,然後是真實的資料值,就放在後面。在讀取這個資料的時候,他會根據變長字段的長度,先讀取出來jack這個值,因為他的長度是4,就讀取4個長度的資料,jack就出來了;然後發現第二個欄位是null,就不用讀取了;第三個欄位是定長字段,直接讀取1個字元就可以了,就是m這個值;第四個欄位是null,不用讀取了;第五個欄位是變長字段長度是9,讀取出來xx_school就可以了。

但實際上我們的資料都是進行編碼之後再儲存的,會在他真實資料部分加入一些隱藏字段:

首先有乙個db_row_id欄位,這就是乙個行的唯一標識,是他資料庫內部給你搞的乙個標識,不是你的主鍵id欄位。如果我們沒有指定主鍵和unique key唯一索引的時候,他就內部自動加乙個row_id作為主鍵。

接著是乙個db_trx_id欄位,這是跟事務相關的,他是說這是哪個事務更新的資料,這是事務id。

最後是db_roll_ptr欄位,這是回滾指標,是用來進行事務回滾的。

所以加上這幾個隱藏字段之後,實際一行資料可能看起來如下所示,這基本就是最終在磁碟上一行資料的樣子了:

0x09 0x04 00000101 0000000000000000000010000000000000011001 00000000094c(db_row_id)00000000032d(db_trx_id) ea000010078e(db_rol_ptr) 616161 636320 6262626262

我們說過乙個資料頁是16kb,那如果一條資料不止16kb呢?比如 varchar(65532),或者text、blob 這種字段、是很可能溢位的,然後資料就會儲存在多個資料頁裡。所以在儲存的資料中,會有乙個指標將這些資料頁串聯起來。

整數是如何儲存在計算機內部的

整數分為正整數和負整數,因此在存入到計算機時需要考慮到正號負號,接下來就討論整數儲存方式。1.無符號表示法 假設計算機的記憶體分配了n個位來讓我們儲存乙個整數,把這n個位稱為乙個儲存單元,那麼使用無符號表示法可以儲存的整數的範圍是0到 2 n 1 步驟分為以下幾步 首先將整數變為二進位制數 如果二進...

如何檢視磁碟儲存空間

最近經常碰到磁碟爆滿的情況,查了下大多數其實大多都是日誌導致的,很多人公用一台伺服器,定期或者自動化清理日誌就是一項很必要的工作了 那麼通過以下步驟可以很快找出儲存大頭 1.如果收到提示磁碟不夠了,先看下整體的磁碟占用情況,直接進入根目錄 cd df lh ps 其實data目錄一開始被沾滿了,這個...

mysql 是怎麼在磁碟上是儲存 NULL 的?

大家知道一般表中char,vachar這些欄位都是有值的,那如果某個字段允許為空,且值確實為空,mysql又是怎麼處理的呢?是不是直接儲存null呢?假設這個欄位的null值我們在磁碟上儲存的時候,就是按照 null 這麼個字串來儲存,是不是很浪費儲存空間?因為字串要占用空間的啊 乙個 null 字...