Ⅰ、InnoDB發展史
時間 | 事件 | 備注 |
---|---|---|
1995 | 由Heikki Tuuri創建Innobase Oy公司,開發InnoDB存儲引擎 | Innobase開始做的是數據庫,希望賣掉該公司 |
1996 | MySQL 1.0 發布 | |
2000 | MySQL3.23版本發布 | |
2001 | InnoDB存儲引擎集成到MySQL數據庫 | 以插件方式集成 |
2006 | Innobase被Oracle公司收購(InnoDB作為開源產品,性能和功能很強大) | InnoDB在被收購后的,MySQL中的InnoDB版本沒有改變 |
2010 | MySQL5.5版本InnoDB存儲引擎稱為默認存儲引擎 | MySQL被Sun收購,Sun被Oracle收購,使得MySQL和InnoDB重新在一起配合開發 |
至今 | 其他存儲引擎已經不再得到Oracle官方的后續開發 |
Ⅱ、InnoDB重要特性一覽
- Fully ACID(InnoDB默認的Repeat Read隔離級別就支持)
- Row-level Locking(支持行鎖)
- Multi-version concurrency control(MVCC)(支持多版本并發控制)
- Foreign key support(支持外鍵)
- Automatic deadlock detection(死鎖自動檢測)
- High performance、High scalability、High availability(高性能,高擴展,高可用)
Ⅲ、物理存儲結構
3.1 主要組成部分
表空間文件:
- 獨立表空間
- 共享表空間
- undo表空間(MySQL5.6開始)
重做日志文件:
- 物理邏輯日志
- 沒有Oracle的歸檔重做日志
3.2 細說表空間文件
表空間的概念:
- 表空間是一個邏輯存儲的概念
- 表空間可以由多個文件組成
- 支持裸設備(可以直接使用O_DIRECT 方式繞過緩存,直接寫入磁盤)
表空間的分類:
①共享表空間(最早只有這個)
- 存儲元數據信息
- 存儲Change Buffer信息
- 存儲Undo信息
- MySQL4.0之前所有數據都是存儲在共享表空間中
②獨立表空間
- MySQL4.0開始,支持每張表對應一個獨立表空間(ibd文件)
- innodb-file-per-table=1(默認為1,這個參數關掉創建表,會發現對應的庫下面沒有該表的idb文件)
- 分區表可以對應多個ibd文件
③undo表空間
- MySQL5.6版本支持獨立的Undo表空間,默認是0,即undo記錄在共享表空間中
- innodb_undo_tablespaces(該值8.0開始將會被剔除,不可修改,默認寫死,為2)
④臨時表空間
- MySQL5.7增加了臨時表空間(ibtmp1)
- innodb_temp_data_file_path
3.3 看下數據目錄
[root@VM_0_5_centos data3306]# ll ib*
-rw-r----- 1 mysql mysql 16285 Feb 4 18:15 ib_buffer_pool
-rw-r----- 1 mysql mysql 79691776 Feb 24 10:53 ibdata1 #共享表空間
-rw-r----- 1 mysql mysql 50331648 Feb 24 10:53 ib_logfile0 #重做日志
-rw-r----- 1 mysql mysql 50331648 Feb 4 15:06 ib_logfile1
-rw-r----- 1 mysql mysql 12582912 Feb 24 10:14 ibtmp1 #臨時表空間
兩個ib_logfile是循環交替寫入的,SSD下盡可能設大,單個文件4G/8G,設置太小可能會導致臟頁刷新時hang住
MySQL中現在只有這兩個文件不會歸檔,最老的3.23版本支持歸檔,因為MySQL有二進制日志所以把這個功能閹割了
[root@VM_0_5_centos data3306]# cd test
[root@VM_0_5_centos test]# ll
total 228
-rw-r----- 1 mysql mysql 65 Feb 4 13:21 db.opt #記錄默認字符集和字符集排序規則
-rw-r----- 1 mysql mysql 8554 Feb 1 15:45 abc.frm #表結構文件
-rw-r----- 1 mysql mysql 98304 Feb 24 10:53 abc.ibd #獨立表空間
就性能而言,獨立和共享速度是一樣的,基本上區別
雖然ibadta1是一個文件,但是在底層申請也是1兆1兆地申請的空間,是連續的(兩種都是以區的方式來管理),在磁盤上的表現是一樣的,1M的數據基本上可以認為是連續的
tips:
sysbench去測8個文件開16個線程和測1個文件開128個線程,測出來iops是一樣的,因為它的分配和管理機制都是一樣的
那為什么MySQL4.0版本開始引入了獨立表空間呢?
主要是為了管理方便,表現為:
- 看上去清晰
- 刪除文件非常簡單,drop完空間可釋放,對于ibdata1來說,一個文件,只能增不能減,刪除一張表只是把這張表對應的空間標為可用,但是空間并不能回收
刪除.ibd文件是不行的,因為對應的innodb元數據表里面的數據沒有刪
.ibd和.ibdata1文件壞了修復的話收費是按行數來算的很貴的哦,相對而言ibdata1文件修復難度更大
切記:不要在數據目錄下刪除任何文件
3.4 小常識
單個ibd文件直接拷貝到新的數據庫中無法直接恢復:
- 原因一:元數據信息還是在ibdata1中
- 原因二:部分索引文件存在于Change Buffer中,目前還是存放于ibdata1文件中
查看表空間的元數據信息
select * from information_schema.innodb_sys_tablespaces;
Ⅳ、邏輯存儲結構
4.1 從上到下的結構
表空間 |
---|
內部有多個段對象(Segment)組成 |
每個段(Segment)由區(Extent)組成 |
每個區(Extent)由頁(Page)組成 |
每個頁里面保存數據(或者叫記錄Row) |
段:
段對用戶來說是透明的,也是一個邏輯的概念,用來組織管理區
在MySQL系統表中是看不到段這個元數據的,但是邏輯上的確存在(重點理解區和頁)
區:
區包含頁
區是最小的空間申請單位,表空間文件要擴展是以區的單位來擴展
區的大小固定為1M,這1M在物理上是連續的,但1M和1M之間不保證連續
page_size=16K 就是1M * 1024 / 16 = 64個頁
16k 64個頁
8k 128個頁
4k 256個頁
通常一次申請4個區大小,1兆1兆地申請,特殊情況會申請5個區,很少發生這種情況
頁 :
等價于ORACLE中的塊 ,最小的I/O操作單位
tips:
data的最小單位不是頁,而是頁中的記錄(row)
普通用戶表中MySQL默認的每個頁為16K
- 從MySQL 5.6開始使用 innodb_page_size可以控制頁大小
- 一旦數據庫通過innodb_page_size創建完成,則后續無法更改
- innodb_page_size 是針對普通表的,壓縮表不受其限制
4.2 來來來,吹兩手
從5.6開始可以調整大小,但只能設置4k和8k,從5.7開始還可以設置32k,64k,如果設置為32k或者64k那區的大小就變為2M和4M
什么淘寶標準MySQL配置參數把這個大小設置為4k,完全扯淡,就算在ssd下4k可能也不會比16k好,另外設置為4k,io操作會變多,不要迷信,現在的表偏向于寬表(很多列組成,單行記錄比較大),一行超過1k甚至4k以上,這時候這個頁大小設為4,性能會很差。
設置小了,B+ tree高度變高,io變多,性能變差;如果是寬列,4k的頁會導致行外存,效率變差
目前為止,大家都用的SSD,列比較寬,內存也比較大,就保留16k或者嘗試一下32k(一些單列特別大的場景,大頁性能有優勢)
tips:
MySQL頁大小和ORACLE頁大小不同的是,MySQL頁大小是全局的,一旦初始化好,就不可再修改,不像ORACLE可以設置每張表的頁大小
Ⅴ、MySQL中如何定位到一個頁
SpaceID
- 每個表空間都對應一個SpaceID,而表空間又對應一個ibd文件,那么一個ibd文件也對應一個SpaceID
- ibdata1對應的SpaceID為0,每創建一個表空間(ibd文件),SpaceID自增長(全局)
PageNumber
- 在一個表空間中,第幾個16K的頁(假設 innodb_page_size = 16K)即為PageNumber
+--------------------+--------------+-------------+--------------+
SpaceID | 0 | 1 | | N | # 全局遞增
+----------------------------------------------------------------+
| | || || |
ibd File | ibdata1 | table1.ibd || ...... || tableX.ibd +------+
| | || || | |
+--------+-----------+-------+-----------------------------------+ |
| | |
| | |
| +--------------------------------------+ |
| | |
| | |
+--------v---+------------+--------------------------------------+ | |
| | | || || | | |
| 16K | 16K | 16K || ...... || 16K | | |
| | | || || | | |
+----------------------------------------------------------------+ | |
Page Number| 0 | 1 | 2 | | M | | |
+------------+------------+-------------+------------+-----------+ | | | | | |
+----------------------------------------------------------+ |
| |
| |
| |
| |
+--------v---+------------+--------------------------------------+ |
| | | || || | |
| 16K | 16K | 16K || ...... || 16K | |
| | | || || | |
+----------------------------------------------------------------+ |
Page Number| 0 | 1 | 2 | | M | |
+------------+------------+-------------+------------+-----------+ | |
|
+--------------------------------------------------------------+
|
|
|
+--------v---+------------+--------------------------------------+
| | | || || |
| 16K | 16K | 16K || ...... || 16K |
| | | || || |
+----------------------------------------------------------------+
Page Number| 0 | 1 | 2 | | M |
+------------+------------+-------------+------------+-----------+
# 每個表空間中,都是從0開始遞增,且僅僅是表空間內唯一
- 每次讀取Page時,都是通過SpaceID和PageNumber進行讀取
- 可以簡單理解為從表空間的開頭讀多少個PageNumber * PageSize的字節(偏移)
- 想成數組,數組的名字就是SpaceID,數組的下標就是PageNumber
- 在一個SpaceID(ibd文件)中,PageNumber是唯一且自增的
- 刪除表的時候,SpaceID不會回收 ,SpaceID是全局自增長的
tips:
這里的區(extent)的概念已經弱化
在這個例子中,第一個區的PageNumber是(0到63)且這64個頁在物理上是連續的;第二個區的PageNumber是(64到127) 且這64個頁在物理上也是連的
但是(0到63)和(64到127)之間在物理上則不一定是連續的,因為區和區之間在物理上不一定是連續的
隨便看看
(root@localhost) [information_schema]> select space, name from information_schema.innodb_sys_tablespaces order by space limit 5;
+-------+---------------------+
| space | name |
+-------+---------------------+
| 2 | mysql/plugin |
| 3 | mysql/servers |
| 4 | mysql/help_topic |
| 5 | mysql/help_category |
| 6 | mysql/help_relation |
+-------+---------------------+
5 rows in set (0.00 sec)
(root@localhost) [information_schema]> select name, space, table_id from information_schema.innodb_sys_tables where space=0;
+------------------+-------+----------+
| name | space | table_id |
+------------------+-------+----------+
| SYS_DATAFILES | 0 | 14 |
| SYS_FOREIGN | 0 | 11 |
| SYS_FOREIGN_COLS | 0 | 12 |
| SYS_TABLESPACES | 0 | 13 |
| SYS_VIRTUAL | 0 | 15 |
+------------------+-------+----------+
5 rows in set (0.00 sec)
(root@localhost) [information_schema]> select name, space, table_id from information_schema.innodb_sys_tables where space<>0 order by space limit 5;
+---------------------+-------+----------+
| name | space | table_id |
+---------------------+-------+----------+
| mysql/plugin | 2 | 16 |
| mysql/servers | 3 | 17 |
| mysql/help_topic | 4 | 35 |
| mysql/help_category | 5 | 36 |
| mysql/help_relation | 6 | 38 |
+---------------------+-------+----------+
5 rows in set (0.00 sec)
- 獨立表空間的table_id和SpaceID一一對應
- 共享表空間是多個table_id對應一個SpaceID
- SpaceID為0的是ibdata1,1這個位置沒有,空的