1 InnoDB存盤引擎
InnoDB存盤引擎最早由Innobase Oy公司開發(屬第三方存盤引擎),從MySQL 5.5版本開始作為表的默認存盤引擎,該存盤引擎是第一個完整支持ACID事務的MySQL存盤引擎,特點是行鎖設計、支持MVCC、支持外鍵、提供一致性非鎖定讀,非常適合OLTP場景的應用使用,目前也是應用最廣泛的存盤引擎,
InnoDB存盤引擎架構包含記憶體結構和磁盤結構兩大部分,總體架構圖如下:
8.0版本:
5.5版本:
2 InnoDB 存盤結構
2.1 磁盤結構
2.1.1 表空間 Tablespaces
InnoDB存盤引擎的邏輯存盤結構是將所有的資料都被邏輯地放在了一個空間中,這個空間中的檔案就是實際存在的物理檔案(.ibd檔案),即表空間,默認情況下,一個資料庫表占用一個表空間,表空間可以看做是InnoDB存盤引擎邏輯結構的最高層,所以的資料都存放在表空間中,例如:表對應的資料、索引、insert buffer bitmap undo資訊、insert buffer 索引頁、double write buffer files 等都是放在共享表空間中的,
表空間分為系統表空間(ibdata1檔案)(共享表空間)、臨時表空間、常規表空間、Undo表空間和file-per-table表空間(獨立表空間),系統表空間又包括雙寫緩沖區(Doublewrite buffer)、Change Buffer等
1.系統表空間 System Tablespace
系統表空間可以對應檔案系統上一個或多個實際的檔案,默認情況下, InnoDB會在資料目錄下創建一個名為.ibdata1,大小為 12M的檔案,這個檔案就是對應的系統表空間在檔案系統上的表示,這個檔案是可以自擴展的,當不夠用的時候它會自己增加檔案大小,需要注意的一點是,在一個MySQL服務器中,系統表空間只有一份,從MySQL5.5.7到MySQL5.6.6之間的各個版本中,我們表中的資料都會被默認存盤到這個系統表空間,
show variables like '%innodb_data_file_path%'
2.獨立表空間
在MySQL5.6.6以及之后的版本中, InnoDB并不會默認的把各個表的資料存盤到系統表空間中,而是為每一個表建立一個獨立表空間,也就是說我們創建了多少個表,就有多少個獨立表空間,使用獨立表空間來存盤表資料的話,會在該表所屬資料庫對應的子目錄下創建一個表示該獨立表空間的檔案,檔案名和表名相同,只不過添加了一個.ibd的擴展名而已,
show variables like '%innodb_file_per_table%'
獨立表空間只是存放資料、索引和插入緩沖Bitmap頁,其他類的資料如回滾(undo)資訊、插入緩沖索引頁、系統事務資訊、二次寫緩沖等還是存放在原來的系統表空間,
3.其他型別的表空間
隨著MySQL的發展,除了上述兩種表空間之外,現在還新提出了一些不同型別的表空間,比如通用表空間 (general tablespace)、undo表空間(undo tablespace)、臨時表空間(temporary tablespace)等
4.表空間結構
表空間又由段(segment)、區( extent)、頁(page)組成,頁是InnoDB磁盤管理的最小單位,在我們執行sql時,不論是查詢還是修改,mysql 總會把資料從磁盤讀取內記憶體中,而且在讀取資料時,不會單獨加在一條資料,而是直接加載資料所在的資料頁到記憶體中,表空間本質上就是一個存放各種頁的頁面池,
「頁」是InnoDB管理存盤空間的基本單位,也是記憶體和磁盤互動的基本單位,也就是說,哪怕你需要1位元組的資料,InnoDB也會讀取整個頁的資料,InnoDB有很多型別的頁,它們的用處也各不相同,比如:有存放undo日志的頁、有存放INODE資訊的頁、有存放Change Buffer資訊的頁、存放用戶記錄資料的頁(索引頁)等等,
InnoDB默認的頁大小是16KB,在初始化表空間之前可以在組態檔中進行配置,一旦資料庫初始化完成就不可再變更了,
SHOW VARIABLES LIKE 'innodb_page_size'
2.1.2 重寫日志 redo log檔案
redo log記錄資料庫的變更,資料庫崩潰后,會從redo log獲取事務資訊,進行系統恢復,redo log在磁盤上表現為ib_logfile0和ib_logfile1兩個檔案,MySQL會在事務的提交前將redo日志重繪回磁盤,
在同一時間提交的事務,會采用組提交(group commit)的方式一次性重繪回磁盤,從而避免一個事務重繪一次磁盤,提高性能,
2.1.3 Double Write Files 雙寫緩沖檔案
double write 是保障 InnoDB 存盤引擎操作資料頁的可靠性,double write 分為兩部分組成,一部分在記憶體中的 double write buffer, 大小為 2MB,另一部分是物理磁盤上共享表空間中連續的128個資料頁,即2個區大小(同樣是2MB),
2.2 記憶體結構
InnoDB存盤引擎是基于磁盤存盤的,并將其中的記錄按照頁的方式進行管理,因此可將其視為基于磁盤的資料庫系統(Disk-base Database),在資料庫中CPU速度與磁盤速度是有很大差距的,基于磁盤的資料庫系統通常使用緩沖池技術來提高資料庫的整體性能,結構如圖所示:
2.1.1 快取池 Buffer Pool
Buffer Pool是InnoDB記憶體中的一塊占比較大的區域,通過記憶體的速度來彌補磁盤速度慢對資料庫性能的影響,在資料庫中進行讀取頁的操作,首先將從磁盤讀到的頁放在緩沖池中,這個程序稱為將頁”FIX”在緩沖池中,下次再讀到相同的頁時,首先判斷該頁是否在緩沖池中,若在緩沖池中,直接讀取該頁,否則讀取磁盤上的頁,
對于資料庫中的頁的修改操作,首先修改在緩沖池中的頁,然后再以一定頻率重繪到磁盤上,這里需要注意的是,頁從緩沖池重繪回磁盤的操作并不是在每次頁發生更新時觸發,而是通過一種稱為Checkpoint的機制重繪回磁盤,
快取區快取的資料頁型別有:索引頁,資料頁,undo頁,插入緩沖(change buffer),自適應哈希索引(adaptive hash index),InnoDB存盤鎖資訊(lock info),資料字典資訊(data dictionary),資料頁和索引頁占據了緩沖池很大部分,
InnoDB1.0.x版本開始,允許有多個緩沖池實體,每個頁根據哈希值平均分配到不同緩沖池的實體中,這樣可以減少資料庫內部資源競爭,增加資料庫的并發處理能力,
show variables like 'innodb_buffer_pool_instances'
整個Buffer Pool的說明用一張圖來概括如下:
1.LRU List,Free List和Flush List——管理InnoDB記憶體區域
為了快取管理的效率,緩沖池被實作為頁鏈表,采用三個鏈表維護記憶體頁,而記憶體頁也因此對應 3 種狀態: Free 尚未使用; Clean 已使用但未修改; Dirty(臟頁)已修改;Free頁只位于Free List,而Clean和Dirty頁同時位于LRU List,Dirty頁只存在于Flush List;
1)LRU List:
資料庫中的緩沖池是通過LRU(Latest Recent Used,最近最少使用)演算法來進行管理的,即最頻繁使用的頁在LRU串列的前端,而最少使用的頁在LRU串列的尾端,當緩沖池不能存放新讀取到的頁時,將首先釋放LRU串列中尾端的頁,
在InnoDB存盤引擎中,緩沖池中頁的大小默認為16KB,同樣使用LRU演算法對緩沖池進行管理,稍有不同的是InnoDB存盤引擎對傳統的LRU演算法做了一些優化,在InnoDB的存盤引擎中,LRU串列中還加入了midpoint位置,新讀取到的頁,雖然是最新訪問的頁,但并不是直接放入到LRU串列的首部,而是放入到LRU串列的midpoint位置,這個演算法在InnoDB存盤引擎下稱為midpoint insertion strategy,在默認配置下,該位置在LRU串列長度的5/8處,
SHOW VARIABLES LIKE'innodb_old_blocks_pct'
引數innodb_old_blocks_pct默認值為37,表示新讀取的頁插入到LRU串列尾端的37%的位置(差不多3/8的位置),在InnoDB存盤引擎中,把midpoint之后的串列稱為old串列,之前的串列稱為new串列,可以簡單地理解為new串列中的頁都是最為活躍的熱點資料
- 那為什么不采用樸素的LRU演算法,直接將讀取的頁放入到LRU串列的首部呢?
這是因為若直接將讀取到的頁放入到LRU的首部,那么某些SQL操作可能會使緩沖池中的頁被重繪出,從而影響緩沖池的效率,常見的這類操作為索引或資料的掃描操作,這類操作需要訪問表中的許多頁,甚至是全部的頁,而這些頁通常來說又僅在這次查詢操作中需要,并不是活躍的熱點資料,如果頁被放入LRU串列的首部,那么非常可能將所需要的熱點資料頁從LRU串列中移除,而在下一次需要讀取該頁時,InnoDB存盤引擎需要再次訪問磁盤,
- 解決熱點資料被移除LRU串列
InnoDB存盤引擎引入了另一個引數來進一步管理LRU串列,這個引數是innodb_old_blocks_time,用于表示頁讀取到mid位置后需要等待多久才會被加入到LRU串列的熱端,通過這個方法盡可能使LRU串列中熱點資料不被刷出,
SHOW VARIABLES LIKE'innodb_old_blocks_time'
當有新的資料從磁盤查詢到記憶體時,會寫入到 old sub list 的頭部,當此資料再次被查詢的時候,即在 old sublist 中命中之后,才會放入 new sublist 的頭部,當頁從LRU串列的old部分加入到new部分時,稱此時發生的操作為page made young;如果因為innodb_old_blocks_time的設定導致頁沒有從old部分移動到new部分的操作,稱為page not made young,
通過命令SHOW ENGINE INNODB STATUS可以觀察到如下內容:
SHOW ENGINE INNODB STATUS
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 10620037
Buffer pool size 8191 // 表示當前緩沖池中記憶體頁的數量,記憶體池的大小=Buffer pool size*16KB
Free buffers 1025 //表示當前FREE串列中頁的數量;
Database pages 6985 //LRU串列中頁的數量;
Old database pages 2558 //
Modified db pages 0 //顯示了臟頁的數量;
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 4656751, not young 61021911 //表示是否發生了頁在LRU佇列上的移動;
0.00 youngs/s, 0.00 non-youngs/s //表示每秒兩類操作發生的次數;
Pages read 1036977, created 686192, written 21243071
0.00 reads/s, 0.00 creates/s, 0.28 writes/s
//表示緩沖池的命中率,正常情況下命中率如果低于95%,則需要觀察是否因為全表掃描引起了LRU佇列被污染的問題
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 6985, unzip_LRU len: 0
I/O sum[17]:cur[0], unzip sum[0]:cur[0]
- 頁壓縮功能
InnoDB存盤引擎從1.0.x版本開始支持壓縮頁的功能,即將原本16KB的頁壓縮為1KB、2KB、4KB和8KB,而由于頁的大小發生了變化,LRU串列也有了些許的改變,對于非16KB的頁,是通過unzip_LRU串列進行管理的,LRU中的頁包含了unzip_LRU串列中的頁,
對于壓縮頁的表,每個表的壓縮比率可能各不相同,可能存在有的表頁大小為8KB,有的表頁大小為2KB的情況,unzip_LRU是怎樣從緩沖池中分配記憶體的呢?
首先,在unzip_LRU串列中對不同壓縮頁大小的頁進行分別管理,其次,通過伙伴演算法進行記憶體的分配,例如對需要從緩沖池中申請頁為4KB的大小,其程序如下:
- 檢查4KB的unzip_LRU串列,檢查是否有可用的空閑頁;
- 若有,則直接使用;
- 否則,檢查8KB的unzip_LRU串列;
- 若能夠得到空閑頁,將頁分成2個4KB頁,存放到4KB的unzip_LRU串列;
- 若不能得到空閑頁,從LRU串列中申請一個16KB的頁,將頁分為1個8KB的頁、2個4KB的頁,分別存放到對應的unzip_LRU串列中,
2)Free List:
free list 定義是當前沒有被使用的記憶體頁,也就是空閑的記憶體頁,當執行查詢操作時,如果頁已經在 buffer pool 中了,則查詢到直接回傳,如果沒有在 buffer pool,并且 free list 不為空,則會從磁盤中查詢對應的資料,放入 free list 的某一頁中,并且把這頁從 free list 中移除,放入 LRU 佇列中,Flush List中的臟頁在執行了刷盤操作后會將空間還給Free List,通過這種方式可以解決空間碎片化
LRU串列用來管理已經讀取的頁,但當資料庫剛啟動時,LRU串列是空的,即沒有任何的頁,這時頁都存放在Free串列中,當需要從緩沖池中分頁時,首先從Free串列中查找是否有可用的空閑頁,若有則將該頁從Free串列中洗掉,放入到LRU串列中,否則,根據LRU演算法,淘汰LRU串列末尾的頁,將該記憶體空間分配給新的頁,
從上面可以看出 【SHOW ENGINE INNODB STATUS】 :
- Free buffers表示當前Free串列中頁的數量,Database pages表示LRU串列中頁的數量,可能的情況是Free buffers與Database pages的數量之和不等于Buffer pool size,因為緩沖池中的頁還可能會被分配給自適應哈希索引、Lock資訊、Change Buffer等頁,而這部分頁不需要LRU演算法進行維護,因此不存在于LRU串列中,
- pages made young顯示了LRU串列中頁移動到前端的次數,youngs/s、non-youngs/s表示每秒這兩類操作的次數,
- 這里還有一個重要的觀察變數——Buffer pool hit rate,表示緩沖池的命中率,通常該值不應該小于95%,若發生Buffer pool hit rate的值小于95%這種情況,用戶需要觀察是否是由于全表掃描引起的LRU串列被污染的問題,
3)Flush List:
在LRU串列中的頁被修改后,稱該頁為臟頁(dirty page),即緩沖池中的頁和磁盤上的頁的資料產生了不一致,這時資料庫會通過CHECKPOINT機制將臟頁重繪回磁盤,而Flush串列中的頁即為臟頁串列,需要注意的是,臟頁既存在于LRU串列中,也存在于Flush串列中,LRU串列用來管理緩沖池中頁的可用性,Flush串列用來管理將頁重繪回磁盤,二者互不影響,
Flush List中的臟頁在執行了刷盤操作后會將空間還給Free List,
同LRU串列一樣,Flush串列也可以通過命令SHOW ENGINE INNODB STATUS來查看,前面例子中Modified db pages 就顯示了臟頁的數量,
2.Checkpoint技術
資料庫在發生增刪查改操作的時候,都是先在buffer pool中完成的,為了提高事物操作的效率,buffer pool中修改之后的資料,并沒有立即寫入到磁盤,這有可能會導致記憶體中資料與磁盤中的資料產生不一致的情況,
倘若每次一個頁的變化,就將新頁的版本重繪到磁盤,那么這個開銷是非常大的,若熱點資料集中在某幾個頁中,那么資料庫的性能就會變得非常差,同時,如果在從緩沖池將頁的的新版本重繪到磁盤時發生了宕機,那么資料就不能恢復了,為了避免這種情況,當前事務資料庫系統普遍都采用了Write Ahead Log策略,即當事務提交時,先寫重做日志,再修改頁,當由于發生宕機而導致資料丟失時,可以通過重做日志來完成資料的恢復,這也是事務ACID中D(Durability持久性)的要求,
checkpoint的作用:
- 縮短資料庫的恢復時間
- 緩沖池不夠用時,將臟頁重繪到磁盤
- 重做日志不可用時,重繪臟頁
checkpoint的分類 - sharp checkpoint:在關閉資料庫的時候,將buffer pool中的臟頁全部重繪到磁盤中,
- fuzzy checkpoint:資料庫正常運行時,在不同的時機,將部分臟頁寫入磁盤,進重繪部分臟頁到磁盤,也是為了避免一次重繪全部的臟頁造成的性能問題,
2.2.2 寫緩沖 Change Buffer
在MySQL5.5之前,叫插入緩沖(Insert Buffer),只針對INSERT做了優化;現在對DELETE和UPDATE也有效,叫做寫緩沖(Change Buffer),它是一種應用在非唯一普通索引頁(non-unique secondary index page)不在緩沖池中,對頁進行了寫操作,并不會立刻將磁盤頁加載到緩沖池,而僅僅記錄緩沖變更(Buffer Changes),等未來資料被讀取時,再將資料合并(Merge)恢復到緩沖池中的技術,寫緩沖的目的是降低寫操作的磁盤IO,提升資料庫性能,
資料的修改分為兩個情況:
1.當修改的資料頁在緩沖池時
上文講過,通過LRU、Flush List的管理,資料庫不是直接寫入磁盤中,是先將redo log寫入到磁盤,再通過checkpoint機制,將這些“臟資料頁”同步地寫入磁盤,等于是將這期間發生的n次的落盤合并成了一次落盤,因為有redo log是落盤的,所以即使資料庫崩潰,快取中的資料頁全部丟失,也可以通過redo log將這些資料頁找回來,
redo log是資料庫用來在崩潰的時候進行資料恢復的日志,redo log的寫入策略可以通過引數控制,并不一定是每一次寫操作之后立即落盤redo log,在部分引數下,redo log可能是每秒集中寫入一次,也有可能采取其他落盤策略,但是無論采用什么方式,redo log的量都是不會減少的,與資料寫入的覆寫性不同,后一條redo log是不會覆寫前一條的,而是增量形式的,因此寫redo log的操作,等同于是對磁盤某一小塊區域的順序I/O,而不像資料落盤一樣的隨機IO在磁盤里寫入,需要磁盤在多個地方移動磁頭,所以redo log的落盤是IO操作當中消耗較少的一種,比資料直接刷回磁盤要優很多,
2.當修改的資料頁不在緩沖池時,不用寫緩沖至少需要下面的三步:
- 先把需要的索引頁,從磁盤加載到緩沖池,一次磁盤隨機讀操作;
- 修改緩沖池中的頁,一次記憶體操作;
- 寫入 redo log ,一次磁盤順序寫操作;
在沒有命中緩沖池的時候,至少多產生一次磁盤IO,對于寫多讀少的業務場景,性能損耗是很高的
加入寫緩沖優化后,流程優化為:
- 在寫緩沖中記錄這個操作,一次記憶體操作;
- 寫入redo log,一次磁盤順序寫操作;
其性能與這個索引頁在緩沖池中,相近,
3.如何保證資料的一致性?
- 資料庫例外奔潰,能夠從redo log中恢復資料;
- 寫緩沖不只是一個記憶體結構,它也會被定期刷盤到寫緩沖系統表空間;
- 資料讀取時,有另外的流程,將資料合并到緩沖池;
下一次讀到該索引頁:
- 載入索引頁,緩沖池未命中,這次磁盤IO不可避免;
- 從寫緩沖讀取相關資訊;
- 恢復索引頁,放到緩沖池LRU和Flush里;(在真正被讀取時,才會被加載到緩沖池中)
4.為什么寫緩沖優化,僅適用于非唯一普通索引頁呢?
InnoDB里有聚集索引(Clustered Index))和普通索引(Secondary Index)兩種,如果索引設定了唯一(Unique)屬性,在 進行修改操作 時, InnoDB必須進行唯一性檢查 ,也就是說, 索引頁即使不在緩沖池,磁盤上的頁讀取無法避免(否則怎么校驗是否唯一!?)
此時就應該直接把相應的頁放入緩沖池再進行修改,
5.除了資料頁被訪問,還有哪些場景會觸發刷寫緩沖中的資料呢?
- 有一個后臺執行緒,會認為資料庫空閑時;
- 資料庫緩沖池不夠用時;
- 資料庫正常關閉時;
- redo log寫滿時;(幾乎不會出現redo log寫滿,此時整個資料庫處于無法寫入的不可用狀態)
6.什么業務場景,適合開啟InnoDB的寫緩沖機制?
- 資料庫大部分是非唯一索引;
- 業務是寫多讀少,或者不是寫后立刻讀取;
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size'
2.2.3 自適應散列索引 Adaptive Hash Index
自適應哈希索參考于優化對BP資料的查詢,InnoDB存盤引擎會監控對二級索引資料的查找,如果觀察到建立哈希索引可以帶來速度的提升(最近連續被訪問三次的資料),則建立哈希索引,自適應哈希索引通過緩沖池的B+樹構造而來,因此建立的速度很快,InnoDB存盤引擎會自動根據訪問的頻率和模式來為某些頁建立哈希索引,(在高負載系統下AHI容易產生資源的爭用,進而引起一些bug導致系統受影響甚至崩潰,故建議關閉該功能)
2.2.4 重做日志緩沖區 rodo Log Buffer
重做日志緩沖區,當在MySQL中對InnoDB表進行資料更改時,這些更改首先存盤在InnoDB日志緩沖區的記憶體中,然后再寫入重做日志(redo logs)的InnoDB日志磁盤檔案中,他讓MySQL在崩潰的時候具有了恢復資料的能力,即在資料庫發生意外的時候,可以進行資料恢復;
日志緩沖區log buffer是記憶體存盤區域,用于保存要寫入磁盤上的日志檔案的資料,日志緩沖區大小由innodb_log_buffer_size 變數定義,默認大小為16MB,
日志緩沖區的內容定期重繪到磁盤,較大的日志緩沖區可以運行大型事務,而無需在事務提交之前將重做日志資料寫入磁盤,因此,如果有更新,插入或洗掉許多行的事務,則增加日志緩沖區的大小可以節省磁盤I/O,
這里還涉及到一個引數 innodb_flush_log_at_trx_commit :控制如何將日志緩沖區的內容寫入并重繪到磁盤,默認為1,不建議修改
- 引數為0時,表示事務commit不立即把 redo log buffer 里的資料刷入磁盤檔案的,而是依靠 InnoDB 的主執行緒每秒(此時間由引數innodb_flush_log_at_timeout控制,默認1s)執行一次重繪到磁盤,此時可能你提交事務了,結果 mysql 宕機了,然后此時記憶體里的資料全部丟失,
- 引數為1時,表示事務commit后立即把 redo log buffer 里的資料寫入到os buffer中,并立即執行fsync()操作
- 引數為2時,表示事務commit后立即把 redo log buffer 里的資料寫入到os buffer中,但不立即fsync()SQL執行程序
什么是binlog
binlog是一個二進制格式的檔案,用于記錄用戶對資料庫更新的SQL陳述句資訊,默認情況下,binlog是二進制格式的,不能使用文本工具的命令進行查看,而是使用mysqlbinlog決議查看,
binlog的功能
當資料寫入到資料庫的時候,會同時把更新的SQL陳述句寫入到相應的binlog檔案里面,同時在使用mysqldump進行備份的時候,只是對一段時間的資料進行了全域備份,但是如果備份后發現資料庫服務器產生故障,這個時候就要用到binlog日志了,
binlog和redolog的區別:
- redo log是在InnoDB存盤引擎層產生,而binlog是mysql資料庫的上層產生,而且binlog是二進制格式的日志,不僅僅針對InnoDB存盤引擎,
- 兩種日志記錄的內容形式不同,MySQL的binlog是邏輯日志,而InnoDB存盤引擎層面的重做日志是物理日志,
- 兩種日志與記錄寫入磁盤的時間點不同,二進制日志只在事物提交完成后進行一次寫入,而redo log的重做日志在事物的進行程序中不斷地被寫入,
- binlog不是回圈使用,在寫滿或者重啟之后,會生成新的binlog檔案,但是redo log是回圈使用的,
3 InnoDB 存盤特性
- 寫緩沖 Change Buffer
- 兩次寫 Double Write
InnoDB在把Dirty 臟頁寫回到表空間之前,在記憶體中會線拷貝到連續的記憶體空間double write buffer緩沖區,然后再把它們寫到一個叫doublewrite buffer file的連續磁盤存盤區域內,在寫doublewrite buffer file完成后,InnoDB才會把Dirty pages寫到data file的適當的位置,如果在寫page的程序中發生意外崩潰,InnoDB在稍后的恢復程序中在doublewrite buffer file中找到完好的page副本用于恢復,
為什么需要雙寫?
InnoDB 的Page Size一般是16KB,其資料校驗也是針對這16KB來計算的,將資料寫入到磁盤是以Page為單位進行操作的,而計算機硬體和作業系統,寫檔案是以4KB(512位元組)作為單位的,不能保證MySQL資料頁面16KB的一次性原子寫,試想,在某個Dirty Page flush的程序中,發生了系統斷電(或者OS崩潰),16K的資料只有部分被寫到磁盤上,只有一部分寫是成功的,這種現象被稱為partial page writes,在出現磁盤崩潰的時候,InnoDB 引擎會從共享表空間中的doublewrite找到該頁的一個副本,將其復制到表空間檔案,再應用重做日志,保障 InnoDB 存盤引擎操作資料頁的可靠性,
為什么不能使用redo log 解決partial page writes?
一旦partial page writes發生,那么在InnoDB恢復時就很尷尬:redo log的頁大小一般設計為512個位元組,因此redo log page本身不會發生break page,用redo log來解決partial write 理論上是可行的,不過innodb的redo log是物理邏輯日志,并不是純物理日志,因此發生partial write后崩潰恢復程序中不能直接應用redo log ,innodb發現break page后實際上會報錯,物理邏輯日志不是完全冪等的,這取決于重做日志型別,對于INSERT產生的日志其不是冪等的,
**
兩次寫的作業流程**
double write由兩部分組成,一部分是InnoDB記憶體中的double write buffer,大小為2MB,另一部分是物理磁盤上的ibdata,系統表空間中大小為2MB,共128個連續的Page(2*1024/16KB=128),即兩個磁區(extend)一個段(segment),其中120個頁用于批量重繪臟頁(如LRU LIST重繪與FLUSH LIST重繪這兩種重繪策略),另外8個頁用于單頁重繪(Single Page Flush),做區分的原因是批量刷臟是后臺執行緒做的,不影響前臺執行緒,而單頁重繪是用戶執行緒發起的,需要盡快的刷臟頁并替換出一個空閑頁出來,
InnoDB重繪(寫出)緩沖區中的資料頁時采用的是一次寫多個頁的方式:
- 多個頁就可以先順序寫入到double write buffer,并呼叫fsync()保證這些資料被重繪到double write磁盤(ibdata),
- 然后資料頁呼叫fsync()被重繪到實際存盤位置;
- 故障恢復時InnoDB檢查double write Buffer與資料頁原存盤位置的內容,若double write頁處于頁斷裂狀態,則簡單的丟棄;若資料頁不一致,則從double write頁還原,
由于double write頁落盤與資料頁落盤在不同的時間點,不會出現double write頁和資料頁同時發生斷裂的情況,因此doublewrite技術可以解決頁斷裂問題,進而保證了重做日志能順利進行,資料庫能恢復到一致的狀態,
3.自適應哈希索引 Adaptive Hash Index
4 參考資料
掘金小冊《MySQL 是怎樣運行的:從根兒上理解 MySQL》學習筆記https://www.jianshu.com/p/3394321c11bf
作者:京東物流 鄧鈞蔚
來源:京東云開發者社區 自猿其說Tech
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/556985.html
標籤:MySQL
上一篇:一站式運維管家 ChengYing 主機接入原理決議
下一篇:返回列表