表類型
對比Oracle支持的各種表類型,InnoDB存儲引擎表更像是Oracle中的索引組織表(index organized table)。在InnoDB存儲引擎表中,每張表都有個主鍵,如果在創建表時沒有顯式地定義主鍵(Primary Key),則InnoDB存儲引擎會按如下方式選擇或創建主鍵。
- 首先表中是否有非空的唯一索引(Unique NOT NULL),如果有,則該列即為主鍵。
- 不符合上述條件,InnoDB存儲引擎自動創建一個6個字節大小的指針。
邏輯存儲結構
InnoDB存儲引擎的邏輯存儲結構和Oracle大致相同,所有數據都被邏輯地存放在一個空間中,我們稱之為表空間(tablespace)。表空間又由段(segment)、區(extent)、頁(page)組成。頁在一些文檔中有時也稱為塊(block),1 extent = 64 pages,InnoDB存儲引擎的邏輯存儲結構大致如圖所示。
表空間
表空間可以看做是InnoDB存儲引擎邏輯結構的最高層,所有的數據都是存放在表空間中。默認情況下InnoDB存儲引擎有一個共享表空間ibdata1,即所有數據都放在這個表空間內。如果我們啟用了參數innodb_file_per_table,則每張表內的數據可以單獨放到一個表空間內。
對於啟用了innodb_file_per_table的參數選項,需要注意的是,每張表的表空間內存放的只是數據、索引和插入緩沖,其他類的數據,如撤銷(Undo)信息、系統事務信息、二次寫緩沖(double write buffer)等還是存放在原來的共享表空間內。這也就說明了另一個問題:即使在啟用了參數innodb_file_per_table之后,共享表空間還是會不斷地增加其大小。
現在我們來做個實驗,實驗之前我已經將innodb_file_per_table設為ON了,看看初始共享表空間文件有多大:
show variables like 'innodb_file_per_table' \G
system ls -lh /usr/local/var/mysql/ibdata1
共享表空間ibdata1的大小為76M。
set autocommit=0;
update mytest set salary=0;
system ls -lh /usr/local/var/mysql/ibdata1
首先將自動提交設為0,即我們需要顯式提交事務(注意,上面結束時我們並沒有commit或者rollback該事務)。接着我們執行會產生大量Undo操作的語句update mytest set salary=0,完成后我們再觀察共享表空間,會發現ibdata1已經增長到了114MB,這就說明了共享表空間中還包含有Undo信息。如果我rollback這個事務,ibdata1這個表空間會不會縮減至原來的58MB大小?我們接下去就來驗證:
rollback;
system ls -lh /usr/local/var/mysql/ibdata1
很“可惜”,還是114MB,即InnoDB存儲引擎不會在rollback時去收縮這個表空間。雖然InnoDB不會幫你回收這些空間,但是MySQL會自動判斷這些Undo信息是否需要,如果不需要,則會將這些空間標記為可用空間,供下次Undo使用。master thread每10秒會執行一次full purge操作。因此很有可能的一種情況是,你再次執行上述的UPDATE語句后,會發現ibdata1不會再增大了,那就是這個原因了。
段
上圖中顯示了表空間是由各個段組成的,常見的段有數據段、索引段、回滾段等。InnoDB存儲引擎表是索引組織的(index organized),因此數據即索引,索引即數據。那么數據段即為B+樹的頁節點(上圖的leaf node segment),索引段即為B+樹的非索引節點(上圖的non-leaf node segment)。
與Oracle不同的是,InnoDB存儲引擎對於段的管理是由引擎本身完成,這和Oracle的自動段空間管理(ASSM)類似,沒有手動段空間管理(MSSM)的方式,這從一定程度上簡化了DBA的管理。
需要注意的是,並不是每個對象都有段。因此更准確地說,表空間是由分散的頁和段組成。
區
區是由64個連續的頁組成的,每個頁大小為16KB,即每個區的大小為1MB。對於大的數據段,InnoDB存儲引擎最多每次可以申請4個區,以此來保證數據的順序性能。
在我們啟用了參數innodb_file_per_talbe后,創建的表默認大小是96KB。區是64個連續的頁,那創建的表的大小至少是1MB才對啊?其實這是因為在每個段開始時,先有32個頁大小的碎片頁(fragment page)來存放數據,當這些頁使用完之后才是64個連續頁的申請。
通過一個實驗來顯示InnoDB存儲引擎對於區的申請:
create table t1 (
col1 int not null auto_increment,
col2 varchar (7000),
primary key(col1)
)engine=InnoDB;
system ls -lh /usr/local/var/mysql/test/t1.ibd
創建了t1表,col2字段設為varchar(7000),這樣能保證一個頁中可以存放2條記錄。可以看到,初始創建完t1后表空間默認大小為96KB.
頁
同大多數數據庫一樣,InnoDB有頁(page)的概念(也可以稱為塊),頁是InnoDB磁盤管理的最小單位。與Oracle類似的是,Microsoft SQL Server數據庫默認每頁大小為8KB,不同於InnoDB頁的大小(16KB),且不可以更改(也許通過更改源碼可以)。
常見的頁類型有:
- 數據頁(B-tree Node)。
- Undo頁(Undo Log Page)。
- 系統頁(System Page)。
- 事務數據頁(Transaction system Page)。
- 插入緩沖位圖頁(Insert Buffer Bitmap)。
- 插入緩沖空閑列表頁(Insert Buffer Free List)。
- 未壓縮的二進制大對象頁(Uncompressed BLOB Page)。
- 壓縮的二進制大對象頁(Compressed BLOB Page)。
行
InnoDB存儲引擎是面向行的(row-oriented),也就是說數據的存放按行進行存放。每個頁存放的行記錄也是有硬性定義的,最多允許存放16KB/2~200行的記錄,即7992行記錄。這里提到面向行(row-oriented)的數據庫,那么也就是說,還存在有面向列(column-orientied)的數據庫。MySQL infobright儲存引擎就是按列來存放數據的,這對於數據倉庫下的分析類SQL語句的執行以及數據壓縮很有好處。類似的數據庫還有Sybase IQ、Google Big Table。面向列的數據庫是當前數據庫發展的一個方向。
物理存儲結構
從物理意義上來看,InnoDB表由共享表空間、日志文件組(更准確地說,應該是Redo文件組)、表結構定義文件組成。若將innodb_file_per_table設置為on,則每個表將獨立地產生一個表空間文件,以ibd結尾,數據、索引、表的內部數據字典信息都將保存在這個單獨的表空間文件中。表結構定義文件以frm結尾,這個是與存儲引擎無關的,任何存儲引擎的表結構定義文件都一樣,為.frm文件。