免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
MySQL原理 - InnoDB引擎 - 行記錄存儲(chǔ) - Compact 行格式

MySQL 服務(wù)器上負(fù)責(zé)對(duì)表中數(shù)據(jù)的讀取和寫入工作的部分是存儲(chǔ)引擎,比如 InnoDB、MyISAM、Memory 等等,不同的存儲(chǔ)引擎一般是由不同的人為實(shí)現(xiàn)不同的特性而開發(fā)的,目前OLTP業(yè)務(wù)的表如果是使用 MySQL 一般都會(huì)使用 InnoDB 引擎,這也是默認(rèn)的表引擎。

為了能說明 InnoDB 引擎的原理,我們必須先搞清楚 InnoDB 的存儲(chǔ)結(jié)構(gòu),通過這些存儲(chǔ)結(jié)構(gòu)才能實(shí)現(xiàn) InnoDB 的事務(wù)特性。

首先我們來看看 InnoDB 表的一行數(shù)據(jù)是如何存儲(chǔ)的。InnoDB是一個(gè)持久化的存儲(chǔ)引擎,也就是數(shù)據(jù)都是保存在磁盤上面的。但是讀寫數(shù)據(jù),對(duì)數(shù)據(jù)處理,這些是發(fā)生在內(nèi)存中。也就是數(shù)據(jù)需要從磁盤讀取到內(nèi)存。那么這個(gè)讀取是如何讀取呢?如果處理哪條數(shù)據(jù),就讀取哪一條到內(nèi)存中,這樣效率也太低了。因?yàn)槊織l數(shù)據(jù)都是一個(gè)硬盤尋址讀取,我們要減少這個(gè)硬盤尋址讀取的次數(shù),可以考慮一塊一塊的讀取數(shù)據(jù),這樣,我們很可能下次請(qǐng)求需要的數(shù)據(jù)就已經(jīng)在內(nèi)存中了,就省去了從硬盤讀取?;谶@個(gè)思想,InnoDB 將一個(gè)表的數(shù)據(jù)劃分成了若干 頁(pages) ,這些頁通過 B-Tree 索引聯(lián)系起來。 每一頁大小默認(rèn)為 16384 Bytes 也就是 16KB (配置為 innodb_page_size )。

同時(shí),這個(gè) B-Tree 索引就是我們經(jīng)常聽到的 聚簇索引(Clustered Index) ,如果表有主鍵,那么主鍵索引就是這個(gè)聚簇索引。通過上面的描述,這個(gè)索引的節(jié)點(diǎn)是包含所有行所有列數(shù)據(jù)的(就是剛剛我們提到的頁)。其他的 二級(jí)索引 的節(jié)點(diǎn)只是有指向主鍵的指針。

對(duì)于比較大的字段,例如 Text 類型的字段,如果也存在于這個(gè)聚簇索引上,那這個(gè)節(jié)點(diǎn)數(shù)據(jù)就會(huì)過大,會(huì)一下子讀取很多頁出來,這樣讀取效率會(huì)降低(例如在我們沒有想讀取這個(gè) Text 列的請(qǐng)求情況下)。所以,InnoDB 對(duì)于變長(zhǎng)字段,一般傾向于將他們存儲(chǔ)在其他地方。至于怎么存儲(chǔ),這個(gè)還和 InnoDB **行格式(InnoDB Row Format)**有關(guān)。行格式一共有四種:Compact、Redundant、Dynamic和Compressed。

我們可以在創(chuàng)建或修改表的語句中指定行格式:

CREATE TABLE 表 ()ROW_FORMAT=行格式;ALTER TABLE 表 ROW_FORMAT=行格式;

Compact 行格式存儲(chǔ)

我們來創(chuàng)建一個(gè)包含幾乎所有基本數(shù)據(jù)類型的表,其他的例如 geometry,timestamp 等等,也是基于 double 還有 bigint 而來的, text、json、blob等類型,一般不與行數(shù)據(jù)一起存儲(chǔ),我們之后再說:

create table record_test_1 (id bigint,score double,name char(4),content varchar(8),extra varchar(16))row_format=compact;

插入如下幾條記錄:

INSERT INTO `record_test_1`(`id`, `score`, `name`, `content`, `extra`) VALUES (1, 78.5, 'hash', 'wodetian', 'nidetiantadetian');INSERT INTO `record_test_1`(`id`, `score`, `name`, `content`, `extra`) VALUES (65536, 17983.9812, 'zhx', 'shin', 'nosuke');INSERT INTO `record_test_1`(`id`, `score`, `name`, `content`, `extra`) VALUES (NULL, -669.996, 'aa', NULL, NULL);INSERT INTO `record_test_1`(`id`, `score`, `name`, `content`, `extra`) VALUES (2048, NULL, NULL, 'c', 'jun');

目前表結(jié)構(gòu):

 ------- ------------ ------ ---------- ------------------ | id    | score      | name | content  | extra            | ------- ------------ ------ ---------- ------------------ |     1 |       78.5 | hash | wodetian | nidetiantadetian || 65536 | 17983.9812 | zhx  | shin     | nosuke           || NULL  |   -669.996 | aa   | NULL     | NULL             ||  2048 | NULL       | NULL | c        | jun              | ------- ------------ ------ ---------- ------------------ 

查看底層存儲(chǔ)文件: record_test_1.ibd ,用16進(jìn)制編輯器打開,我這里使用的是 Notepad 和他的 HEX-Editor 插件??梢哉业饺缦碌臄?shù)據(jù)域(可能會(huì)有其中 mysql 生成的行數(shù)據(jù)不一樣,但是我們創(chuàng)建的行數(shù)據(jù)內(nèi)容應(yīng)該是一樣的,而且數(shù)據(jù)長(zhǎng)度應(yīng)該是一摸一樣的,可以搜索其中的字符找到這些數(shù)據(jù)):

我們這里先直接給出這些數(shù)據(jù)代表的意義,讓大家直觀感受下:

變長(zhǎng)字段長(zhǎng)度列表:10 08 Null值列表:00 記錄頭信息:00 00 10 00 47 隱藏列DB_ROW_ID:00 00 00 00 08 0c 隱藏列DB_TRX_ID:00 00 00 03 c9 4d 隱藏列DB_ROLL_PTR:b9 00 00 01 2d 01 10 列數(shù)據(jù)id(1):80 00 00 00 00 00 00 01 列數(shù)據(jù)score(78.5):00 00 00 00 00 a0 53 40 列數(shù)據(jù)name(hash):68 61 73 68 列數(shù)據(jù)content(wodetian):77 6f 64 65 74 69 61 6e 列數(shù)據(jù)extra(nidetiantadetian):6e 69 64 65 74 69 61 6e 74 61 64 65 74 69 61 6e 變長(zhǎng)字段長(zhǎng)度列表:06 04 Null值列表:00 記錄頭信息:00 00 18 00 37 隱藏列DB_ROW_ID:00 00 00 00 08 0d 隱藏列DB_TRX_ID:00 00 00 03 c9 4e 隱藏列DB_ROLL_PTR:ba 00 00 01 2f 01 10 列數(shù)據(jù)id(65536):80 00 00 00 00 01 00 00 列數(shù)據(jù)score(17983.9812):b5 15 fb cb fe 8f d1 40 列數(shù)據(jù)name(zhx):7a 68 78 20 列數(shù)據(jù)content(shin):73 68 69 6e 列數(shù)據(jù)extra(nosuke):6e 6f 73 75 6b 65 Null值列表:19 記錄頭信息:00 00 00 00 27 隱藏列DB_ROW_ID:00 00 00 00 08 0e 隱藏列DB_TRX_ID:00 00 00 03 c9 51 隱藏列DB_ROLL_PTR:bc 00 00 01 33 01 10 列數(shù)據(jù)score(-669.996):87 16 d9 ce f7 ef 84 c0 列數(shù)據(jù)name(aa):61 61 20 20 變長(zhǎng)字段長(zhǎng)度列表:03 01 Null值列表:06 記錄頭信息:00 00 28 ff 4b 隱藏列DB_ROW_ID:00 00 00 00 08 0f 隱藏列DB_TRX_ID:00 00 00 03 c9 54 隱藏列DB_ROLL_PTR:be 00 00 01 3d 01 10 列數(shù)據(jù)id(2048):80 00 00 00 00 00 08 00 列數(shù)據(jù)content(c):63 列數(shù)據(jù)extra(jun):6a 75 6e

可以看出,在 Compact 行記錄格式下,一條 InnoDB 記錄,其結(jié)構(gòu)如下圖所示:

Compact 行格式存儲(chǔ) - 變長(zhǎng)字段長(zhǎng)度列表

對(duì)于像 varchar, varbinary,text,blob,json以及他們的各種類型的 可變長(zhǎng)度字段 ,需要將他們到底占用多少字節(jié)存儲(chǔ)起來,這樣就省去了列數(shù)據(jù)之間的邊界定義,MySQL 就可以分清楚哪些數(shù)據(jù)屬于這一列,那些不屬于。Compact行格式存儲(chǔ),開頭就是變長(zhǎng)字段長(zhǎng)度列表,這個(gè)列表包括 數(shù)據(jù)不為NULL 的每個(gè) 可變長(zhǎng)度字段 的長(zhǎng)度,并按照列的順序 逆序 排列。

例如上面的第一條數(shù)據(jù):

 ------- ------------ ------ ---------- ------------------ | id    | score      | name | content  | extra            | ------- ------------ ------ ---------- ------------------ |     1 |       78.5 | hash | wodetian | nidetiantadetian | ------- ------------ ------ ---------- ------------------ 

有兩個(gè) 數(shù)據(jù)不為NULL 的字段 content 和 extra ,長(zhǎng)度分別是 8 和 16,轉(zhuǎn)換為 16 進(jìn)制分別是:0x08,0x10。倒序的順序排列就是 10 08

這是對(duì)于長(zhǎng)度比較短的情況,用一字節(jié)表示長(zhǎng)度即可。如果變長(zhǎng)列的內(nèi)容占用的字節(jié)數(shù)比較多,可能就需要用2個(gè)字節(jié)來表示。那么什么時(shí)候用一個(gè)字節(jié),什么時(shí)候用兩個(gè)字節(jié)呢?

我們給這張表加一列來測(cè)試下:

alter table `record_test_1` add column `large_content` varchar(1024) null after `extra`;

這時(shí)候行數(shù)據(jù)部分并沒有變化。

  • 如果 字符集的最大字節(jié)長(zhǎng)度 (我們這里字符集是latin,所以長(zhǎng)度就是1)乘以 字段最大字符個(gè)數(shù) (就是varchar里面的參數(shù),我們這里的 large_content 就是1024) < 255,那么就用一個(gè)字節(jié)表示。這里對(duì)于 large_content ,已經(jīng)超過了255.
  • 如果超過255,那么:如果 字段 真正 占用字節(jié)數(shù) < 128,就用一個(gè)字節(jié)如果 字段 真正 占用字節(jié)數(shù) >= 128,就用兩個(gè)字節(jié)

問題一:那么為什么用 128 作為分界線呢? 一個(gè)字節(jié)可以最多表示255,但是 MySQL 設(shè)計(jì)長(zhǎng)度表示時(shí),為了區(qū)分是否是一個(gè)字節(jié)表示長(zhǎng)度,規(guī)定,如果最高位為1,那么就是兩個(gè)字節(jié)表示長(zhǎng)度,否則就是一個(gè)字節(jié)。例如,01111111,這個(gè)就代表長(zhǎng)度為 127,而如果長(zhǎng)度是 128,就需要兩個(gè)字節(jié),就是 10000000 10000000,首個(gè)字節(jié)的最高位為1,那么這就是兩個(gè)字節(jié)表示長(zhǎng)度的開頭,第二個(gè)字節(jié)可以用所有位表示長(zhǎng)度,并且需要注意的是,MySQL采取 Little Endian 的計(jì)數(shù)方式,低位在前,高位在后,所以 129 就是 10000001 10000000。同時(shí),這種標(biāo)識(shí)方式,最大長(zhǎng)度就是 2^15 - 1 = 32767,也就是32 KB。

問題二:如果兩個(gè)字節(jié)也不夠表示的長(zhǎng)度,該怎么辦? innoDB 頁大小默認(rèn)為 16KB,對(duì)于一些占用字節(jié)數(shù)非常多的字段,比方說某個(gè)字段長(zhǎng)度大于了16KB,那么如果該記錄在單個(gè)頁面中無法存儲(chǔ)時(shí),InnoDB會(huì)把一部分?jǐn)?shù)據(jù)存放到所謂的溢出頁中,在變長(zhǎng)字段長(zhǎng)度列表處只存儲(chǔ)留在本頁面中的長(zhǎng)度,所以使用兩個(gè)字節(jié)也可以存放下來。這個(gè)溢出頁機(jī)制,我們后面和Text字段一起再說。

然后對(duì)第一行數(shù)據(jù)填充 large_content 字段,對(duì)于第二行,將新字段更新為空字符串。

update `record_test_1` set `large_content` = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' where id = 1;update `record_test_1` set `large_content` = '' where id = 1;

查看數(shù)據(jù):

發(fā)現(xiàn) COMPACT 行記錄格式下,對(duì)于變長(zhǎng)字段的更新,會(huì)使原有數(shù)據(jù)失效,產(chǎn)生一條新的數(shù)據(jù)在末尾。

第一行數(shù)據(jù)原有的被廢棄,記錄頭發(fā)生變化,主要是打上了刪除標(biāo)記,這個(gè)稍后我們就會(huì)提到。第一行新數(shù)據(jù):

變長(zhǎng)字段長(zhǎng)度列表:82 80 10 08 Null值列表:00 記錄頭信息:00 00 30 01 04 隱藏列DB_ROW_ID:00 00 00 00 08 0c 隱藏列DB_TRX_ID:00 00 00 03 c9 6e 隱藏列DB_ROLL_PTR:4f 00 00 01 89 1c 51 列數(shù)據(jù)id(1):80 00 00 00 00 00 00 01 列數(shù)據(jù)score(78.5):00 00 00 00 00 a0 53 40 列數(shù)據(jù)name(hash):68 61 73 68 列數(shù)據(jù)content(wodetian):77 6f 64 65 74 69 61 6e 列數(shù)據(jù)extra(nidetiantadetian):6e 69 64 65 74 69 61 6e 74 61 64 65 74 69 61 6e 列數(shù)據(jù)large_content(abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz):61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a

可以看到,變長(zhǎng)字段長(zhǎng)度列表變成了 82 80 10 08 ,這里的 large_content 字符編碼最大字節(jié)大小為1,字段字符最大個(gè)數(shù)為1024,這里第一行記錄這個(gè)字段字符數(shù)量是130,所以應(yīng)該用兩個(gè)字節(jié)。130*1轉(zhuǎn)換成16進(jìn)制為 0x82 也就是 0x02 0x80,最高位標(biāo)識(shí)1之后,就是 0x82 0x80,對(duì)應(yīng)咱們的變長(zhǎng)字段長(zhǎng)度列表的開頭。

而新的第二行,變長(zhǎng)字段長(zhǎng)度列表變成了 00 06 04 ,因?yàn)閷?shí)際 large_content 占用了0個(gè)字節(jié)。

Compact 行格式存儲(chǔ) - NULL 值列表

某些字段可能可以為 NULL,如果對(duì)于 NULL 還單獨(dú)存儲(chǔ),是一種浪費(fèi)空間的行為,和 Compact 行格式存儲(chǔ)的理念相悖。采用 BitMap 的思想,標(biāo)記這些字段,可以節(jié)省空間。Null值列表就是這樣的一個(gè) BitMap。

NULL 值列表僅僅針對(duì)可以為 NULL 的字段,如果一個(gè)字段標(biāo)記了 not null ,那么這個(gè)字段不會(huì)進(jìn)入這個(gè) NUll 值列表的 BitMap 中。

NULL值列表占用幾個(gè)字節(jié)呢?每個(gè)不為 NULL 的字段,占用一位, 每超過八個(gè)字段,就是 8 位,就多一個(gè)字節(jié) ,不足一個(gè)字節(jié),高位補(bǔ)0。假如一個(gè)表所有字段都是 not null ,那么就沒有NULL 值列表,也就占用 0 個(gè)字節(jié)。并且,每個(gè)字段在這個(gè) bitmap 中,類似于變長(zhǎng)字段長(zhǎng)度列表,是 逆序排列 的。

 ------- ------------ ------ ---------- ------------------ ------------------------------------------------------------------------------------------------------------------------------------ | id    | score      | name | content  | extra            | large_content                                                                                                                      | ------- ------------ ------ ---------- ------------------ ------------------------------------------------------------------------------------------------------------------------------------ |     1 |       78.5 | hash | wodetian | nidetiantadetian | abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz || 65536 | 17983.9812 | zhx  | shin     | nosuke           | lex                                                                                                                                || NULL  |   -669.996 | aa   | NULL     | NULL             | NULL                                                                                                                               ||  2048 | NULL       | NULL | c        | jun              | NULL                                                                                                                               | ------- ------------ ------ ---------- ------------------ ------------------------------------------------------------------------------------------------------------------------------------ 

針對(duì)第一第二行記錄,由于沒有為 NULL 的字段,所以他們的 NULL 值列表為00. 針對(duì)第三行記錄,他的 NULL 字段分別是 id , content , extra , large_content ,分別是第一,第四,第五,第六列,那么 NULL 值列表為:00111001,也就是 0x39。在加入新字段之前NULL 字段分別是 id , content , extra ,分別是第一,第四,第五列,那么 NULL 值列表為:00011001,也就是 0x19 針對(duì)第四行記錄,他的 NULL 字段分別是 score , name , large_content ,分別是第二,第三,第六列,那么 NULL 值列表為:00100110,也就是 0x26。在加入新字段之前NULL 字段分別是 score , name ,分別是第二,第三列,那么 NULL 值列表為:00000110,也就是 0x06。

Compact 行格式存儲(chǔ) - 記錄頭信息

對(duì)于Compact 行格式存儲(chǔ),記錄頭固定為5字節(jié)大?。?/p>

名稱大?。╞its)描述無用位2目前沒用到deleted_flag1記錄是否被刪除min_rec_flag1B 樹中非葉子節(jié)點(diǎn)最小記錄標(biāo)記n_owned4該記錄對(duì)應(yīng)槽所擁有記錄數(shù)量heap_no13該記錄在堆中的序號(hào),也可以理解為在堆中的位置信息record_type3記錄類型,普通數(shù)據(jù)記錄為000,節(jié)點(diǎn)指針類型為001,偽記錄首記錄 infimum 行為010,偽記錄最后一個(gè)記錄 supremum 行為011,1xx的為保留的next_record pointer16頁中下一條記錄的相對(duì)位置

對(duì)于更新前的第一行和第二行:

第一行記錄頭信息:00 00 10 00 47 轉(zhuǎn)換為2進(jìn)制:00000000 00000000 00010000 00000000 01000111無用位:00,deleted_flag:0,min_rec_flag:0,n_owned:0000,heap_no:0000000000010,record_type:000,next_record:00000000 01000111第二行記錄頭信息:00 00 18 00 37 轉(zhuǎn)換為2進(jìn)制:00000000 00000000 00011000 00000000 00110111無用位:00,deleted_flag:0,min_rec_flag:0,n_owned:0000,heap_no:0000000000010,record_type:000,next_record:00000000 01000111

對(duì)于更新后的原始第一行和第二行:

第一行記錄頭信息:20 00 10 00 47 轉(zhuǎn)換為2進(jìn)制:00010000 00000000 00010000 00000000 01000111無用位:00,deleted_flag:1,min_rec_flag:0,n_owned:0000,heap_no:0000000000010,record_type:000,next_record:00000000 01000111第二行記錄頭信息:20 00 18 00 37 轉(zhuǎn)換為2進(jìn)制:00010000 00000000 00011000 00000000 00110111無用位:00,deleted_flag:1,min_rec_flag:0,n_owned:0000,heap_no:0000000000010,record_type:000,next_record:00000000 01000111

可以看出,原有的數(shù)據(jù) deleted_flag 變成 1,代表數(shù)據(jù)被刪除。

對(duì)于更新后的新的第一行和第二行:

第一行記錄頭信息:00 00 30 00 ca 轉(zhuǎn)換為2進(jìn)制:00000000 00000000 00110000 00000000 11001010無用位:00,deleted_flag:0,min_rec_flag:0,n_owned:0000,heap_no:0000000000011,record_type:000,next_record:00000000 11001010第二行記錄頭信息:00 00 38 fe e6轉(zhuǎn)換為2進(jìn)制:00000000 00000000 00111000 11111110 11100110無用位:00,deleted_flag:0,min_rec_flag:0,n_owned:0000,heap_no:0000000000111,record_type:000,next_record:11111110 11100110

這些信息的其他字段,在我們之后用到的時(shí)候,會(huì)詳細(xì)說明。

Compact 行格式存儲(chǔ) - 隱藏列

隱藏列包含三個(gè):

列名大?。ㄗ止?jié))描述DB_ROW_ID6主鍵ID,這個(gè)列不一定會(huì)生成。優(yōu)先使用用戶自定義主鍵作為主鍵,如果用戶沒有定義主鍵,則選取一個(gè) Unique 鍵作為主鍵,如果表中連 Unique 鍵都沒有定義的話,則會(huì)為表默認(rèn)添加一個(gè)名為 DB_ROW_ID 的隱藏列作為主鍵DB_TRX_ID6產(chǎn)生當(dāng)前記錄項(xiàng)的事務(wù)id,每開始一個(gè)新的事務(wù)時(shí),系統(tǒng)版本號(hào)會(huì)自動(dòng)遞增,而事務(wù)開始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為事務(wù)id,事務(wù) commit 的話,就會(huì)更新這里的 DB_TRX_IDDB_ROLL_PTR7undo log 指針,指向當(dāng)前記錄項(xiàng)的 undo log,找之前版本的數(shù)據(jù)需通過此指針。如果事務(wù)回滾的話,則從 undo Log 中把原始值讀取出來再放到記錄中去

這里我們先不詳細(xì)展開這些列的說明,只是先知道這些列即可,只會(huì)會(huì)在聚簇索引說明以及多版本控制分析的章節(jié)中詳細(xì)說明。

Compact 行格式存儲(chǔ) - 數(shù)據(jù)列 bigint 存儲(chǔ)

對(duì)于 bigint 類型,如果 不為 NULL,則占用8字節(jié) ,首位為符號(hào)位,剩余位存儲(chǔ)數(shù)字,數(shù)字范圍是 -2^63 ~ 2^63 - 1 = -9223372036854775808 ~ 9223372036854775807。 如果為 NULL,則不占用任何存儲(chǔ)空間 。

存儲(chǔ)時(shí),如果為正數(shù),則首位 bit 為1,如果為負(fù)數(shù),則首位為 0 并用補(bǔ)碼的形式存儲(chǔ)。

對(duì)于我們的四行數(shù)據(jù):

第一行列數(shù)據(jù)id(1):80 00 00 00 00 00 00 01 第二行列數(shù)據(jù)id(65536):80 00 00 00 00 01 00 00 第三行行列數(shù)據(jù)id(NULL):空第四行列數(shù)據(jù)id(2048):80 00 00 00 00 00 08 00 

其他的類似的整數(shù)存儲(chǔ),tinyint(1字節(jié)),smallint(2字節(jié)),mediumint(3字節(jié)),int(4字節(jié))等,只是字節(jié)長(zhǎng)度上面有區(qū)別。對(duì)應(yīng)的無符號(hào)類型,tinyint unsigned,smallint unsigned, mediumint unsigned,int unsigned,bigint unsigned等等,僅僅是是否有符號(hào)位的區(qū)別。

同時(shí),這里提一下 bigint(20) 里面這個(gè) 20 的作用。他只是限制顯示,和底層存儲(chǔ)沒有任何關(guān)系。整型字段有個(gè) zerofill 屬性,設(shè)置后(例如 bigint(20) zerofill ),在數(shù)字長(zhǎng)度不夠 20 的數(shù)據(jù)前面填充0,以達(dá)到設(shè)定的長(zhǎng)度。這個(gè) 20 就是顯示長(zhǎng)度的設(shè)定。

Compact 行格式存儲(chǔ) - 數(shù)據(jù)列 double 存儲(chǔ)

double 的存儲(chǔ)對(duì)于非 NULL 的列,符合 IEEE 754 floating-point 'double format' bit layout 這個(gè)統(tǒng)一標(biāo)準(zhǔn):

  • 最高位 bit 表示符號(hào)位(0x8000000000000000)
  • 第二到第十二的 bit 表示指數(shù)(0x7ff0000000000000)
  • 剩下的 bit 表示浮點(diǎn)數(shù)真正的數(shù)字(0x000fffffffffffffL)

同時(shí),Innodb存儲(chǔ)在數(shù)據(jù)文件上的格式為 Little Edian ,需要進(jìn)行反轉(zhuǎn)后,才能取得字段的真實(shí)值。 同樣的,如果為 NULL, 則不占用空間。

例如:

第一行列數(shù)據(jù)score(78.5):00 00 00 00 00 a0 53 40翻轉(zhuǎn): 40 53 a0 00 00 00 00 00二進(jìn)制: 01000000 01010011 10100000 00000000 00000000 00000000 00000000 00000000符號(hào)位:0,指數(shù)位10000000101 = 1029,減去階數(shù) 1023 = 實(shí)際指數(shù) 6,小數(shù)部分0.0011101000000000000000000000000000000000000000000000,轉(zhuǎn)換為十進(jìn)制為0.125 0.0625 0.03125 0.0078125 = 0.2265625, 加上隱含數(shù)字 1 1.2265625, 之后乘以 2 6 次方就是 1.2265625 * 64 = 78.5

計(jì)算過程較為復(fù)雜,可以利用 Java 的 Double.longBitsToDouble() 轉(zhuǎn)換:

public static void main(String[] args) {    System.out.println(Double.longBitsToDouble(0x4053a00000000000L));}

輸出為 78.5

類似的類型,float,也是相同的格式,只是長(zhǎng)度減半。

Compact 行格式存儲(chǔ) - 數(shù)據(jù)列 char 存儲(chǔ)

對(duì)于定長(zhǎng)字段,不需要存長(zhǎng)度信息直接存儲(chǔ)數(shù)據(jù)即可,如果不足設(shè)定的長(zhǎng)度則補(bǔ)充。對(duì)于char類型,補(bǔ)充 0x20, 對(duì)應(yīng)的就是空格。

例如:

第一行列數(shù)據(jù)name(hash):68 61 73 68 第二行列數(shù)據(jù)name(zhx):7a 68 78 20 第三行列數(shù)據(jù)name(aa):61 61 20 20 第四行列數(shù)據(jù)name(NULL):空

對(duì)于類似的 binary 類型,補(bǔ)充 0x00。

Compact 行格式存儲(chǔ) - 數(shù)據(jù)列 varchar 存儲(chǔ)

因?yàn)閿?shù)據(jù)開頭有可變長(zhǎng)度字段長(zhǎng)度列表,所以 varchar 只需要保存實(shí)際的數(shù)據(jù)即可,不需要填充額外的數(shù)據(jù)。

正是由于這個(gè)特性,對(duì)于可變長(zhǎng)度字段的更新,一般都是將老記錄標(biāo)記為刪除,在記錄末尾添加新的一條記錄填充更新后的記錄。這樣提高了更新速度,但是增加了存儲(chǔ)碎片。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
InnoDB的行記錄格式, Compact, Redundant, Compressed, Dynamic
驗(yàn)證一個(gè)小小的問題
? MySQL數(shù)據(jù)庫InnoDB存儲(chǔ)引擎在線加字段實(shí)現(xiàn)原理詳解
MySQL 如何存儲(chǔ)長(zhǎng)度很長(zhǎng)的數(shù)據(jù)字段
MySQL Innodb 存儲(chǔ)結(jié)構(gòu) & 存儲(chǔ)Null值(是否存溢出段,查詢查看)
MySQL面試總結(jié)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服