成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL 的一行記錄是怎么存儲的?

數(shù)據(jù)庫 MySQL
mysql 的一行記錄,最終肯定是存儲在磁盤上,也就是肉眼可見的文件上,今天我們的目標(biāo)很簡單,就是看看它到底是怎么存的。

[[413088]]

 我們先準(zhǔn)備好三樣?xùn)|西。

1. 準(zhǔn)備好一個 mysql 5.7 并將其啟動。

2. 準(zhǔn)備好 mysql 的官方文檔放在旁邊:

https://dev.mysql.com

3. 準(zhǔn)備好 mysql 的源碼,萬一要用呢,別怕:

https://dev.mysql.com/downloads/mysql/5.7.html

一手資料,就是官方文檔 + 源碼 + 二進制文件,其中二進制文件是我們自己去磁盤中找的,一會就知道了。Let's Go!mysql 會把文件存在哪里呢?先找到他。

  1. mysql> SHOW VARIABLES LIKE 'datadir'
  2. +---------------+---------------------------------------------+ 
  3. | Variable_name | Value                                       | 
  4. +---------------+---------------------------------------------+ 
  5. | datadir       | C:\ProgramData\MySQL\MySQL Server 5.7\Data\ | 
  6. +---------------+---------------------------------------------+ 
  7. 1 row in set, 1 warning (0.00 sec) 

我是 windows,就在這里了,進入這個目錄。

這些是啥先不管,盯著它看就好了,我們繼續(xù)。

第一步:創(chuàng)建數(shù)據(jù)庫

  1. mysql> create database flash; 

盯著剛剛的文件夾看,此時會多出一個文件夾

同時這個文件夾里會多出一個文件,叫:

  1. |-- flash 
  2.     |-- db.opt 
  3. |-- flash 
  4. |-- performance_schema 

看一眼它里面的內(nèi)容,就知道他是干嘛的了。

  1. default-character-set=latin1 
  2. default-collation=latin1_swedish_ci 

default-character-set 是默認字符集,default-collation 是默認字符序。字符集大家都了解,就不展開了。字符序就是字符的排序和比較規(guī)則,一般以 _ci 結(jié)尾的表示大小寫不敏感,_cs 結(jié)尾的表示大小寫敏感,_bin 結(jié)尾的表示用編碼值進行比較。含義知道了,那我們重新設(shè)置它應(yīng)該會有所變化,我們把這個數(shù)據(jù)庫設(shè)置為開發(fā)時常用的 utf8mb4 格式。

  1. ALTER SCHEMA `flash` DEFAULT CHARACTER SET utf8mb4; 

再看 db.opt 文件,內(nèi)容已經(jīng)發(fā)生了變化。

  1. default-character-set=utf8mb4 
  2. default-collation=utf8mb4_general_ci 

OK,那我們現(xiàn)在對這個文件有了個初步認識,創(chuàng)建一個新的數(shù)據(jù)庫時,首先會多出一個以數(shù)據(jù)庫名為名稱的文件夾,然后文件夾里面會多出一個描述數(shù)據(jù)庫配置的 db.opt 文件,我們繼續(xù)!

第二步:創(chuàng)建表

創(chuàng)建一張 student 表,三列,其中 id 是主鍵。

  1. CREATE TABLE `flash`.`student` ( 
  2.   `id` INT NOT NULL
  3.   `nameVARCHAR(10) NOT NULL
  4.   `age` INT NULL
  5.   PRIMARY KEY (`id`) 
  6. ENGINE = InnoDB 
  7. DEFAULT CHARACTER SET = utf8mb4; 

此時 flash 文件夾中,多出了兩個文件|-- flash

  1. |-- flash 
  2.     |-- db.opt     
  3.     |-- student.frm 
  4.     |-- student.ibd 
  5. |-- flash 
  6. |-- performance_schema 

為了嚴(yán)謹(jǐn),我們先看下 db.opt 文件有沒有變化,發(fā)現(xiàn)沒有任何變化,說明創(chuàng)建表對這個 db.opt 配置信息文件,沒有影響。

再點開 student.frm,壞了,亂碼了。

說明這個文件不是文本文件,用二進制方式打開它。

我把一些關(guān)鍵的地方都標(biāo)上了含義,那這個文件的作用大家就一目了然了,就是記錄表結(jié)構(gòu)嘛,具體的格式可以看 frm 文件結(jié)構(gòu)的官方文檔(寫得太復(fù)雜了...我反正是沒看):https://dev.mysql.com/doc/internals/en/frm-file-format.html

db.opt 記錄了數(shù)據(jù)庫信息,student.frm 記錄了表結(jié)構(gòu)信息,那重頭戲自然就在那個 student.ibd 文件了,這里一定存著具體的數(shù)據(jù)呀,索引呀等信息吧。

打開它!

果不其然還是亂碼,那還是二進制打開它!截取了中間信息較為豐富的某部分。

發(fā)現(xiàn)一點也看不懂。

第三步:插入數(shù)據(jù)

我們加幾條數(shù)據(jù)看看。

  1. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('1''dibingfa2''2'); 
  2. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('2''dibingfa2''2'); 
  3. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('3''dibingfa3''2'); 
  4. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('4''dibingfa4''2'); 
  5. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('5''dibingfa5''2'); 
  6. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('6''dibingfa6''2'); 
  7. INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('7''dibingfa7''2'); 

再二進制打開它!

發(fā)現(xiàn)有些東西我們可以看出點端倪了!猜測下這部分就是每一行的記錄信息吧。

我們插入了七條數(shù)據(jù),我發(fā)現(xiàn)這些二進制串有一段可以分割成七對,我把他單獨拿出來,并且按行分割。

我們將第一行記錄拆解,第一行記錄的表數(shù)據(jù)是這樣的。

1 dibingfa 2

在 ibd 文件中是這樣的。

08 00 00 00 10 00 24 80 00 00 01 00 00 00 00 0A 07 A7 00 00 01 1B 01 10 64 69 62 69 6E 67 66 61 80 00 00 02

這串?dāng)?shù)據(jù)代表啥意思呢?由于本文只能參考官方文檔,我們看這里,即 Innodb 行格式。

https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html

看這部分(我們的行格式是 DYNAMIC 類型,不過參考 COMPACT 類型描述也行,因為幾乎一樣,之后再說):

不要看這么一大長串就害怕,我們一點點來,別著急。

這個文檔,我可能沒資格評價,但我個人覺得寫的很爛,一大堆廢話也沒說明白格式是什么樣子,每個字節(jié)表示什么。不過也可能作用并非如此。

好的官方文檔應(yīng)該是能把每個字節(jié)和每一位都解釋清楚的,無奈這個文檔不行,那我們就去找更接近一手資料的源碼。

我找到了源碼,還是很清晰的,注釋上就寫明了每一行記錄的磁盤數(shù)據(jù)格式,太好了,不用看代碼了。

再貼上剛剛的第一行記錄。

08 00 00 00 10 00 24 80 00 00 01 00 00 00 00 0A 07 A7 00 00 01 1B 01 10 64 69 62 69 6E 67 66 61 80 00 00 02

來一點點看,第一部分。

| length of the last non-null variable-length field of data ... ...|

...

| length of first variable-length field of data |

這部分是變長字段長度列表,就是依次記錄所有變長字段的長度,由于我們只有一個變長字段 varchar(10) 的 name,所以就是 08,我們存儲的 "dibingfa" 剛好是 8 個字節(jié),對上了。那如果是多個,很顯然,就這樣存。

錯!應(yīng)該是這樣存,也就是逆序存放,具體為啥后面說。

OK,這就是第一個字節(jié) 08 所表示的含義。

再往下。

| SQL-null flags (1 bit per nullable field), padded to full bytes |

第二個結(jié)構(gòu)叫 NUll 值列表,用 1 位表示一個 NULL 值,要填充滿一個字節(jié),那往下的一個字節(jié)是 00,一看我們的記錄中也確實沒有 NULL 值,對上了。

具體來說,同樣也是逆序存放的。

繼續(xù)。

| 4 bits used to delete mark a record, and mark a predefined

minimum record in alphabetical order |

| 4 bits giving the number of records owned by this record

(this term is explained in page0page.h) |

| 13 bits giving the order number of this record in the

heap of the index page |

| 3 bits record type: 000=conventional, 001=node pointer (inside B-tree),

010=infimum, 011=supremum, 1xx=reserved |

| two bytes giving a relative pointer to the next record in the page |

ORIGIN of the record

這五個字節(jié)很亂,放在一塊叫記錄頭信息,00 00 10 00 24,其表示刪除狀態(tài),記錄類型,下一條記錄的相對位置等。

這一大坨先放一放,因為涉及到好多額外的知識。繼續(xù)往下看。

| first field of data |

...

| last field of data |

剩下全都是具體的列數(shù)據(jù)了,從第一列到最后一列。第一列是 ID 列,是 INT 類型的 1,占四個字節(jié) 80 00 00 01。開頭的 80 是因為,正數(shù)要以 1 開頭,這是 mysql 規(guī)定的,0x80 的二進制就是 1000 0000,所以這也對上了。第二列是 name 列,是 "dibingfa" 這樣一個 varchar 類型的字符串。可是與后面怎么也對應(yīng)不上,這是咋回事呢?還記不記得,mysql 每行記錄會有幾個隱藏列,rowid,事務(wù) ID,回滾指針?沒錯,就是他們。其中,因為有主鍵,所以 rowid 就不存在了,也可以說第一列要么是 mysql 為我們生成的 6 字節(jié)的 rowid,要么是用戶定義的主鍵或其他 Unique 鍵,優(yōu)先以用戶定義的鍵為準(zhǔn)。下面我們一塊看一下這五個列。(三個隱藏列,兩個我們定義的列)

主鍵 ID:80 00 00 01

事務(wù) ID:00 00 00 00 0A 07

回滾指針:A7 00 00 01 1B 01 10

name 列(dibingfa):64 69 62 69 6E 67 66 61

age 列(2):80 00 00 02

其中 age 列同剛剛說的一樣,mysql 會為正數(shù)的前面,加一個 1,所以 age 為 2,在磁盤上存儲的就是 80 00 00 02。事務(wù) ID 和回滾指針就涉及到事務(wù)、隔離級別和 MVCC 這一大坨八股文的知識點,這里不做展開。

行記錄格式整體結(jié)構(gòu)

總結(jié)下,整個一行記錄的格式,叫做 mysql 的行記錄格式,ROW_FORMAT。這個 ROW_FORMAT 可以有不同的值,代表存儲這一行記錄的不同數(shù)據(jù)結(jié)構(gòu),其枚舉記錄在 remOtypes.h 文件中。

  1. /** Innodb row types are a subset of the MySQL global enum row_type. 
  2. They are made into their own enum so that switch statements can account 
  3. for each of them. */ 
  4. enum rec_format_enum { 
  5.  REC_FORMAT_REDUNDANT = 0, /*!< REDUNDANT row format */ 
  6.  REC_FORMAT_COMPACT = 1, /*!< COMPACT row format */ 
  7.  REC_FORMAT_COMPRESSED = 2, /*!< COMPRESSED row format */ 
  8.  REC_FORMAT_DYNAMIC = 3 /*!< DYNAMIC row format */ 
  9. }; 

我電腦上用的是 mysql 5.7,其默認的行記錄格式是 DYNAMIC,這個在源碼中也可以找到答案,在 ha_innodb.cc 中。

  1. static ulong innodb_default_row_format = DEFAULT_ROW_FORMAT_DYNAMIC; 

當(dāng)然,可以用如下命令查詢你的行格式。

  1. show table status from flash like 'student'

所以我們今天以上講述的格式,都是 DYNAMIC 格式的結(jié)構(gòu),總結(jié)起來如下:

記錄源信息

  • 變長字段列表
  • NULL 值列表
  • 記錄頭信息

具體記錄的各列信息

  • rowid 或 主鍵(隱藏列)
  • 事務(wù) ID(隱藏列)
  • 回滾指針(隱藏列)
  • 列 1
  • 列 2
  • ...
  • 列 n

剛剛那七條記錄,整體分析下,就如下圖。

然后多個行,一次緊密地排列,通過記錄頭中的下一條記錄的相對位置指針信息,可以快速找到下一條記錄的起始位置。再宏觀一點看,整個 ibd 文件,劃分了很多個塊,每個塊 16 KB,我們這幾行記錄信息,在第四個塊的某個區(qū)域內(nèi)。

 

具體為什么是這個區(qū)域呢?因為這里是用戶記錄部分,前面還有文件頭、頁面頭等信息,共占用 120 個字節(jié),咱們今天講的行記錄部分,就從第 121 個字節(jié)開始。慢慢的,我再和大家一起把其他部分搞清楚,那 mysql 這塊我們就從最原始的磁盤數(shù)據(jù)入手,將 data 文件夾下的所有文件都搞清楚了。

最原始的數(shù)據(jù)都搞清楚了,原理還擔(dān)心么?

再聊幾句

其實,不要被行記錄格式這種名詞嚇到,它只是個協(xié)議或規(guī)定罷了。就是 mysql 規(guī)定了一種將一行記錄存儲在磁盤中的格式,以便于 mysql 自己的程序可以根據(jù)這個結(jié)構(gòu)認識這一行記錄。

所以這種協(xié)議,首先要滿足讓 mysql 知道全部想知道的信息,比如 mysql 現(xiàn)在能僅僅通過 ibd 文件里的這些二進制數(shù),知道每個字段的值都是什么嗎?不能,因為它不知道表結(jié)構(gòu)是什么樣子,也就沒法知道兩個字段值之間的界限在哪里。

所以不難想到,它一定利用了 frm 文件中存儲的表結(jié)構(gòu)信息。

其次,要讓 mysql 在知道這些信息的同時,還能更方便地利用這個結(jié)構(gòu),占用更少的存儲空間,以及提升程序的便利性。

拿占用更少存儲空間這塊來講,NULL 值完全可以當(dāng)做普通列,也存儲在后面,然后規(guī)定一個 NULL 值的二進制標(biāo)識符即可。但 DYNAMIC 行記錄格式規(guī)定前面放一個 NULL 值列表的結(jié)構(gòu),并且僅僅用 1 位來表示一個 NULL 值記錄,這樣就極大節(jié)省了空間。再說便利性這塊,上面說了變長字段長度列表和 NULL 值列表,都是逆序存儲的,看似很別扭,其實就是為了程序的便利性,這里留給大家自己探索吧。

 

責(zé)任編輯:華軒 來源: 低并發(fā)編程
相關(guān)推薦

2022-11-30 17:13:05

MySQLDynamic存儲

2025-04-01 01:25:00

MySQLInnoDBMyISAM

2017-04-05 11:10:23

Javascript代碼前端

2020-07-15 09:40:37

代碼Python瀏覽記錄

2024-08-01 09:57:17

DELETE死鎖工具

2021-11-02 16:25:41

Python代碼技巧

2024-07-04 00:30:17

2016-12-02 08:53:18

Python一行代碼

2011-08-25 09:17:31

Java調(diào)用存儲過程返回一行或多行結(jié)果集

2020-08-12 14:54:00

Python代碼開發(fā)

2019-08-30 12:02:23

數(shù)據(jù)工程師云廠商

2021-11-05 06:57:50

架構(gòu)工具代碼

2020-09-23 09:40:17

內(nèi)存Python代碼

2018-12-18 10:23:45

Python代碼內(nèi)存

2020-08-24 08:25:48

Python開發(fā)工具

2020-05-15 09:32:50

TB數(shù)據(jù)Elasticsear

2022-02-23 14:37:48

代碼Pythonbug

2021-01-25 09:36:00

Python代碼文件

2009-07-23 09:20:25

Javascript代

2014-12-18 10:01:09

代碼
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 精品国产一区二区国模嫣然 | 免费成人在线网站 | 亚洲高清av在线 | 欧美激情欧美激情在线五月 | 日韩一二区在线观看 | 成人在线观看免费观看 | 亚洲一区二区免费 | 亚洲第一视频网 | 欧美一区二区二区 | 久久久久无码国产精品一区 | 天天爱天天操 | 伊人久久在线 | 日韩欧美一区二区三区四区 | 成人精品在线视频 | 中文字幕高清一区 | 成人av免费 | av官网在线| 特a毛片| 精品国产乱码久久久久久蜜柚 | 毛片免费在线 | 精品三级在线观看 | 久热久草 | 草草草网站| 久久精品中文字幕 | 日韩在线一区二区 | 91av视频在线观看 | 日本网站免费观看 | 亚洲导航深夜福利涩涩屋 | 蜜桃av人人夜夜澡人人爽 | 欧美精品福利视频 | 日韩欧美在线观看一区 | 91麻豆精品国产91久久久更新资源速度超快 | 亚洲高清在线 | 成人国产在线视频 | 在线国产一区二区 | 九九热精 | 天堂va在线观看 | 99爱在线视频 | 国产做a爱免费视频 | 干干干操操操 | 拍真实国产伦偷精品 |