一、邏輯存儲結構
(1)表空間(ibd文件):一個mysql實例可以對應多個表空間,用于存儲記錄、索引等數據。
cd /var/lib/mysql
(2)段,分為數據段(leaf node segment)、索引段(non-leaf node segment)、回滾段(rollback segment),InnoDB是索引組織表,數據段就是B+樹的非葉子節點。段用來管理多個extent(區)。
(3)區,表空間的單元結構,每個區的大小為1M,默認情況下,InnoDB存儲引擎頁大小為16K,即一個區中一共有64個連續的頁。
(4)頁,是InnoDB存儲引擎磁盤管理的最小單元,每個頁的大小默認為16KB。為了保證頁的連續性,InnoDB存儲引擎每次從磁盤申請4-5個區。
(5)行,InnoDB存儲引擎數據是按行進行存放的。
二、架構
1、架構圖
MySQL5.5版本開始,默認使用InnoDB存儲引擎,它擅長事務處理,具有崩潰恢復特性,在日常開發中使用非常廣泛。
InnoDB架構圖,左側內存結構,右側磁盤結構。
2、內存結構
(1)buffer pool:緩沖池
dirty page:緩沖區的頁還沒有刷新到磁盤上。
(2)change buffer:更改緩沖區
對唯一索引和主鍵索引不會操作更改緩沖區。
在執行增刪改語句時,不是直接操作到磁盤,而是將數據變更存放在更改緩沖區,再以一定頻率將將數據放入緩沖池,再刷新到磁盤中。
(3)log buffer:日志緩沖區
(4)adaptive hash index:自適應哈希索引
InnoDB默認不支持哈希索引,默認B+索引。哈希索引不支持范圍查詢,支持等值匹配。
3、磁盤結構
(1)system tablespace:系統表空間
參數:innodb_data_file_path
(2)file-per-table tablespaces(每張表獨立的表空間)
參數:innodb_file_per_table
(3)general tablespaces(通用表空間)
需要通過create tablespace語法創建通用表空間,在創建表時,可以指定該表空間。
語法:
create tablespace xxx add datafile ·file_name· engine = engine_name;
create table xxx tablespace ts_name;
cd /var/lib/mysql
(4)undo tablespace:撤銷表空間
(5)temporary tablespace:臨時表空間
(6)doublewrite buffer files:雙寫緩沖區
?
(7)redo log:重做日志
以循環方式寫入重做日志文件,涉及兩個文件:
4、后臺線程
后臺線程:在合適的時機,將InnoDB存儲引擎緩沖池中的數據刷新到磁盤文件中。
(1)master thread
核心后臺線程,負責調度其他線程,還負責將緩沖池中的數據異步刷新到磁盤中,保持數據的一致性,還包括臟頁的刷新、合并插入緩存、undo頁的回收。
(2)IO Thread
在InnoDB存儲引擎中大量使用了AIO來處理IO請求,可以極大地提高數據庫的性能,而IO Thread主要負責這些IO請求的回調。
(3)Purge Thread
主要用于回收事務已經提交了的undo log,在事務提交之后,undo log可能不用了,就用它來回收。
(4)page cleaner thread
協助master thread刷新臟頁到磁盤的現場,它可以減輕master thread的工作壓力,減少阻塞。
三、事務原理
1、事務原理概述
事務特性:
2、redo log:重做日志
redo log實現持久性
當客戶端發起事務操作時,先操作緩沖區,在緩沖區查找是否有我們要操作的數據,沒有數據則通過后臺線程去磁盤讀取該數據,緩存在緩沖區中,進行增刪改操作后,將增刪改的數據直接寫入redo log buffer,記錄數據頁變化,然后直接將其刷新在磁盤文件redo log file內。過段時間進行臟頁刷新時,若程序出錯,可通過redo log進行恢復。
為WAL(write-ahead logging)先寫日志
每隔一段時間會清理redo log file,因此,這兩個文件是循環寫的。
3、undo log:回滾日志(Ctrl+Z)
undolog實現原子性
在進行update操作時,undo log記錄更新前的數據。
undo log銷毀:當我們在進行回滾操作后,undo log就沒有用了,會進行銷毀,但不會立即刪除,因為還有可能用于MVCC(多版本并發控制)。
4、MVCC(多版本并發控制)
(1)基本概念
①當前讀:
讀取的是記錄的最新版本,讀取時還要保證其他并發事務不能修改當前記錄,會對讀取的記錄進行加鎖。對于我們日常的操作,如:select ... lock in share mode(共享鎖),select ... for update、update、insert、delete(排他鎖)都是一種當前讀。
演示:
開啟兩個客戶端,在第一個客戶端執行select * from student;
在第二個客戶端執行update操作:
此時在第一個客戶端執行select * from student;無法查詢到更新后的數據,第二個客戶端commit;后也無法查詢到更新后的語句,要讀取最新的數據,即當前讀,要執行select * from student lock in share mode;
②快照讀
前文在第一個客戶端執行select * from student;無法查詢到更新后的數據,第二個客戶端commit;后也無法查詢到更新后的語句,即為快照讀。
repeatable read:
在一個事務中第一次執行select * from student;為快照讀的地方,后面執行的select * from student為讀取前面的快照數據。
③MVCC(multi-version concurrency control)
三個隱式字段、undo log、readview
(2)MVCC-隱式字段
創建了一張表,含三個字段id、age、name,在InnoDB引擎中還會增加三個字段
使用ibd2sdi student.ibd查詢表結構
可以看到兩個隱式字段,由于這張表有主鍵,第三個隱式字段不會被自動創建
(3)MVCC-undo log
有數據如下所示:
要執行下面的事務:
首先要執行事務2,update操作,現在undo log日志中記錄更新前的數據:
然后再修改數據;
同理執行事務2:
執行事務3:
undo log版本鏈:
(4)MVCC-readview
讀取記錄由readview決定
(5)MVCC-RC級別
每一次執行快照讀時生成readview
分析事務5:
當第一次執行查詢id為30的記錄時,readview為如下所示:
由于事務2已經提交了,所以當前活躍事務為3,4,5
事務5要查詢數據,快照讀為他生成的,creator_trx_id=5
帶入db_trc_id=4,都不行,沿著版本鏈帶入db_trc_id=3,失敗
帶入=2時,滿足第二條,查詢到這條:
當第二次查詢id為30的記錄時,readview為如下所示:
依次帶入:
查詢到這條數據:
(6)MVCC-RR級別
RR隔離級別下,僅在事務第一次執行快照讀時生成readview,后續復用該readview。
事務5中,兩次查詢id為30的記錄,兩次readview都如下所示:
規則和上面一樣,兩次查詢到的為: