Mysql架構圖
存儲引擎負責對表中的數據的進行讀取和寫入,常用的存儲引擎有InnoDB、MyISAM、Memory等,不同的存儲引擎有自己的特性,數據在不同存儲引擎中存放的格式也是不同的,比如Memory都不用磁盤來存儲數據。
在InnoDB中,數據會存儲到磁盤上,在真正處理數據時需要先將數據加載到內存,表中讀取某些記錄時,InnoDB存儲引擎不需要一條一條的把記錄從磁盤上讀出來,InnoDB采取的方式是:將數據划分為若干個頁,以頁作為磁盤和內存之間交互的基本單位,InnoDB中頁的大小一般為 16 KB,也就是說,當需要從磁盤中讀數據時每一次最少將從磁盤中讀取16KB的內容到內存中,每一次最少也會把內存中的16KB內容寫到磁盤中。
InnoDB數據頁結構
頁是InnoDB管理存儲空間的基本單位,一個頁的大小默認是16KB。
SHOW GLOBAL STATUS like 'Innodb_page_size';
頁結構:
名稱 | 中文名 | 占用空間(字節) | 描述 |
File Header | 文件頭部 | 38 | 頁的一些通用信息 |
Page Header | 頁面頭部 | 56 | 數據頁專有的一些信息 |
Infimum + Supremum | 最小記錄和最大記錄 | 26 | 兩個虛擬的行記錄 |
User Records | 用戶記錄 | 不確定 | 實際存儲的行記錄內容 |
Free Space | 空閑空間 | 不確定 | 頁中尚未使用的空間 |
Page Directory | 頁面目錄 | 不確定 | 頁中的某些記錄的相對位置 |
File Trailer | 文件尾部 | 8 | 校驗頁是否完整 |
InnoDB行格式
一行記錄可以以不同的格式存在InnoDB中,行格式分別是Compact、Redundant、Dynamic和Compressed行格式。
我們可以在創建或修改表的語句中指定行格式:
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名稱 ALTER TABLE 表名 ROW_FORMAT=行格式名稱
Compact行格式
變長字段長度列表:
MySQL支持一些變長的數據類型,比如VARCHAR(M)、VARBINARY(M)、TEXT類型,BLOB類型,這些數據類型修飾列稱為變長字段,變長字段中存儲多少字節的數據不是固定的,所以我們在存儲真實數據的時候需要順便把這些數據占用的字節數也存起來。在Compact行格式中,把所有變長字段的真實數據占用的字節長度都存放在記錄的開頭部位,從而形成一個變長字段長度列表。
CHAR是一種固定長度的類型,VARCHAR則是一種可變長度的類型。 VARCHAR(M),M代表最大能存多少個字符。( MySQL5.0.3以前是字節,以后就是字符)
NULL值列表(標志位):
Compact行格式會把可以為NULL的列統一管理起來,存一個標記為在NULL值列表中,如果表中沒有允許存儲NULL 的列,則 NULL值列表也不存在了。
- 二進制位的值為1時,代表該列的值為NULL
- 二進制位的值為0時,代表該列的值不為NULL
記錄頭信息:
除了變長字段長度列表、NULL值列表之外,還有一個用於描述記錄的記錄頭信息,它是由固定的5個字節組成。
5個字節也就是40個二進制位,不同的位代表不同的意思,如圖:
真實數據
記錄的真實數據除了我們自己定義的列的數據以外,還會有三個隱藏列:
實際上這幾個列的真正名稱其實是:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR。 一個表沒有手動定義主鍵,則會選取一個Unique鍵作為主鍵,如果連Unique鍵都沒有定義的話,則會為表默認添加一個名為row_id的隱藏列作為主鍵。所以row_id是在沒有自定義主鍵以及Unique鍵的情況下才會存在的。
行溢出數據
VARCHAR(M)類型的列最多可以占用65535個字節。其中的M代表該類型最多存儲的字符數量,如果我們使用ascii字符集的話,一個字符就代表一個字節,我們看看VARCHAR(65535)是否可用:
mysql> CREATE TABLE varchar_size_demo( -> c VARCHAR(65535) -> ) CHARSET=ascii ROW_FORMAT=Compact; ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs mysql>
報錯信息表達的意思是:MySQL對一條記錄占用的最大存儲空間是有限制的,除BLOB或者TEXT類型的列之外,其他所有的列(不包括隱藏列和記錄頭信息)占用的字節長度加起來不能超過65535個字節。這個65535個字節除了列本身的數據之外,還包括一些其他的數據,比如說我們為了存儲一個VARCHAR(M)類型的列,其實需要占用3部分存儲空間:
1. 真實數據
2. 變長字段真實數據的長度
3. NULL值標識
如果該VARCHAR類型的列沒有NOT NULL屬性,那最多只能存儲65532個字節的數據,因為變長字段的長度占用2個字節,NULL值標識需要占用1個字節。
mysql> CREATE TABLE varchar_size_demo( -> c VARCHAR(65532) -> ) CHARSET=ascii ROW_FORMAT=Compact; Query OK, 0 rows affected (0.02 sec) CREATE TABLE varchar_size_demo( c VARCHAR(65533) not null ) CHARSET=ascii ROW_FORMAT=Compact; Query OK, 0 rows affected (0.02 sec)
記錄中的數據太多產生的溢出
一個頁的大小一般是16KB,也就是16384字節,而一個VARCHAR(M)類型的列就最多可以存儲65533個字節,這樣就可能出現一個頁存放不了一條記錄。
在Compact和Reduntant行格式中,對於占用存儲空間非常大的列,在記錄的真實數據處只會存儲該列的一部分數據,把剩余的數據分散存儲在幾個其他的頁中,然后記錄的真實數據處用20個字節存儲指向這些頁的地址(當然這20個字節中還包括這些分散在其他頁面中的數據的占用的字節數),從而可以找到剩余數據所在的頁。
Dynamic和Compressed行格式
這兩種行格式類似於COMPACT行格式,只不過在處理行溢出數據時有點兒分歧,它們不會在記錄的真實數據處存儲一部分數據,而是把所有的數據都存儲到其他頁面中,只在記錄的真實數據處存儲其他頁面的地址。另外,Compressed行格式會采用壓縮算法對頁面進行壓縮。