扒一扒InnoDB數據在硬盤上是如何存放的

前言

hello,小夥伴們下午好。俗話說的好,一時拖更一時爽,一直拖更一直爽。可是今天良心發現了,決定要更一下。高能預警,爲了還債,特意寫了篇長文。數據庫


索引組織表

在InnoDB存儲引擎中,表都是按照主鍵順序組織存放的,這種存儲方式的表被稱爲索引組織表。bash

在InnoDB中,每張表都有各自的主鍵(Primary Key),若是在建立表的時候顯式的定義主鍵,則InnoDB存儲引擎會按以下方式選擇或建立主鍵。post

  • 首先判斷表中是否有非空的索引,若是有則第一個定義的非空索引做爲主鍵
  • 若是不符合上述條件,InnoDB存儲引擎自動建立一個6個字節大小的指針
這樣的描述太乾癟啦,咱們來動手操做下。

1.選擇第一個定義的非空索引

首先,咱們建立表student,並填充兩條測試數據,語句以下:性能

create table student(
a int ,
b int not null,
c int not null,
UNIQUE key (a),
UNIQUE key (c),
UNIQUE key (b)
);
insert into student select 1,2,3;
insert into student select 4,5,6;複製代碼

運行結果以下,咱們能夠看出_rowid的值等於列c的值,那就說明當前存儲的結構是將c做爲主鍵的。另外a是能夠爲空的,雖然他定義惟一鍵的是第一個,但仍然不會做爲主鍵。b雖然是先定義列,可是定義惟一鍵是在c以後,因此也不會被做爲惟一鍵。測試


2.自動建立6個字節大小的指針

首先,咱們建立表score,並填充兩條測試數據,語句以下:
ui

create table score(
a int ,
b int ,
c int,
UNIQUE key (a),
UNIQUE key (c),
UNIQUE key (b)
);
insert into score select 1,2,3;
insert into score select 4,5,6;複製代碼

運行結果以下,直接報錯了,不認爲_rowid是他的列名,由於沒有知足條件的列能成爲他的主鍵,因此其就自動建立指針來解決問題啦。spa


InnoDB的邏輯存儲結構(總體)

表空間

表空間能夠看作是InnoDB存儲引擎邏輯結構的最高層,因此的數據都存放在表空間裏面。.net

在默認狀況下,InnoDB存儲引擎有一個共享表空間ibdata1,即全部數據都存放在這個表空間裏面。固然也能夠在my.ini參數文件中啓用innodb_file_per_table,則每張表的數據就能夠單獨放在一個表空間中。設計

注意:即便啓用innodb_file_per_table參數,一些回滾信息,系統事務信息等仍是在共享的表空間中,其隨着時間的變化大小仍是會不斷增大的。單獨的表空間只會存放一些數據及索引信息。3d

在InnoDB存儲引擎中,對段的管理都是由引擎自身所完成的,DBA不能也不必對其進行控制。

區是由連續頁組成的空間,在任何狀況下每一個區的大小都是1MB,頁的大小爲16kb,因此一個區一共有64個連續的頁。

下面詳細描述。

下面詳細描述。

InnoDB行記錄格式(重點)

InnoDB存儲引擎和大多數數據庫同樣,記錄是以行的形式存儲的,這就意味着頁中保存着表中的一行行數據。那麼問題就來了,他這一行數據是包括哪些部分,除了具體的數據,還有其餘的一些額外信息嗎?

首先,咱們先來看一下若是沒有指定行格式,其默認的行格式是什麼?

新建表test,擁有的字段a,b ,c 。

create table test(
a varchar(10),
b varchar (10) not null,
c char(10)
);
insert into test values( '001','Andy','compter');
insert into test values( '002','Bob',NULL);複製代碼

以下圖所示,默認的行格式是Compact ,該行格式是在MySQL5.0中引入的,其設計目標是高校的存儲數據。簡單來講,一個頁存放的行數據越多,其性能越高。針對這個描述,咱先放在一邊,以後看到其餘的行格式,咱對比着看,爲啥compact性能高?


下圖爲行格式Compact的大概結構,先瞅一眼,主要分爲兩個部分,額外信息和真實數據。額外信息包括變成字段長度,NULL值列表,記錄頭信息,真實數據即爲該行記錄有多少列,每列數據有哪些。其中記錄頭信息包括記錄刪除位,記錄類型,下一個指針的位置。

注意,記錄頭信息還有不少爲其餘信息,可是重要的就這幾個。是否是不知道他們是幹撒的,一臉懵逼中,沒事,這仍是混個臉熟,下一部分來慢慢盤他。


變長字段長度

MySQL支持一些變長的數據類型,如varchar,text,blob,變長長度存儲多少字節的數據是不固定的,因此咱們在存儲真實數據的時候,須要將這些數據佔用的字節數也要存儲起來。

剛纔咱們新增了兩條數據,先拿第一個數據爲例,將真正數據佔用的字節長度都存放在記錄的開頭部位,從而造成一個變長字段長度列表,逆序存放。以下圖,因此最終第一條記錄存放的十六進制爲08 04 03,他們之間沒有空格,是爲了顯示的效果才加了空格。那第二條記錄很明顯是03 03.

注意:若是表中沒有變長字段,則該字段不存在。


NULL值列表

咱們知道表中的某些列可能存儲NULL值,若是這些NULL值放在記錄的真實數據中存儲會佔用空間,因此Compact將這些值爲NULL的列統一管理起來,存儲在NULL表中。

注意:跟變長字段同樣,若是表中沒有NULL值的列,則該字段不存在。

注意:MySQL規定NULL值列表必須是整數個字節的位表示,若是使用的二進制位歌手不是整數個字節,則在字節的高位補0.

第一行數據雖然沒有NULL值,可是a,c是可能存儲NULL值的列,因此NULL值列表以下,0表示列所對應的值不爲NULL,1表示列所對應的值爲NULL。


第二行數據a不是NULL,c是NULL,因此對應的NULL值列表以下。


記錄頭信息

  • 記錄刪除位(delete_mask):0爲未刪除,1爲刪除
  • 記錄類型(record_type):0表示普通記錄,1表示B+樹非葉子節點記錄,2表示最小記錄,3表示最大記錄。
  • 下一個指針的位置(next_record):表示從當前記錄的真實數據到下一條記錄的真實數據之間的地址偏移量。好比第一條記錄的next_record爲20,那麼意味從第一條記錄的真實數據的地址處向後找32個字節即是下一條記錄的真實數據。實際上就是鏈表結構。

真實數據

InnoDB數據頁結構(重點)

下圖爲數據頁的總體結構,咱先了解個大概,再慢慢盤他。


文件頭(File Header)

頁的一些通用信息,包括該頁屬於哪一個表空間,InnoDB存儲引擎頁的類型。

頁頭(Page Header)

記錄數據頁的狀態信息。

最小記錄+最大記錄(Infimum+supermum)

在InnoDB存儲引擎中,每一個數據頁都有兩條虛擬的行記錄,用來限定記錄的邊界。

Infimum記錄是指比該頁中任何主鍵值都要小的值,Supermum記錄是指比該頁中任何主鍵值都要大的值。

這兩個值在頁建立時都會被的建立,而且在任何狀況下不會被刪除。

這兩條記錄的構造十分簡單,都是有5個字節大小的記錄頭信息和8個字節的固定部分組成的。

注意:上面提到了最小記錄的record_type爲2,最大記錄的record_type爲3。

用戶記錄(User Records)

重點來了,這邊是數據實際存儲位置,上面已經針對行格式作了詳細的劃分,如今咱就長話短說啦。

若是我刪除了第二行記錄,這條記錄並非馬上刪除了,只是將刪除記錄位改成1啦。而且將他前面一條數據的指針指向他後面一條數據的地址,從而跳過這一條數據。

至於爲何會這樣作呢?是爲了節約時間和空間的消耗。若是在刪除的時候,馬上從磁盤上移除,那麼其餘記錄在磁盤上從新排列須要性能消耗,因此在刪除的時候,只會將全部被刪除的記錄組成一個垃圾鏈表,稍後操做。或者有新紀錄插入的時候,覆蓋掉剛纔的存儲空間。




空閒空間(Free Space)

不重要。

頁面目錄(Page Directory)

咱們如今已經找到記錄在頁面中按照主鍵由小到達順序組成一個單鏈表,那若是想根據主鍵查詢頁中的某條記錄怎麼辦?

最蠢的方法確定是按單鏈表的順序從頭至尾的查找,由於只有知道前面一條記錄的記錄的地址,才能根據指針找到下一條記錄。可是這個有個明顯的缺點,就是太慢了,若是有1000條數據,一個個的查詢,若是最後一條記錄才知足條件,那就太浪費時間啦。

咱們能夠先從順序表中想一想,若是順序表中要找一個記錄,咱們除了從頭開始查以外,還能夠採用二分法,能夠提高查詢速度。

那麼在單鏈表中是否能夠採用二分法呢?答案是確定的。即採用目錄的形式,將全部的記錄劃分爲多個記錄塊,而後取每一個記錄塊的最大的值,將其組成一個目錄,在查找的時候,先查目錄,能判斷在哪一個區間內。這個過程就相似於在書中找到某一個概念,要從目錄先找同樣。

文章尾部(File Tailer)

不重要。哈哈哈。

結束

碼字不易,請多多關注哦。

寫在最後

該篇借鑑的博文,書籍包括以下,特此感謝!

《MySQL技術內幕——InnoDB存儲引擎》

MySQL是如何運行的

blog.csdn.net/u010922732/…

juejin.im/post/5cb3e3…

相關文章
相關標籤/搜索