mysql的頁斷裂(partial write)問題與(double write)技術 1. 頁斷裂 2. double write 推薦閱讀

1. 頁斷裂

1.1 什麼叫做頁斷裂(partial write)

頁斷裂是數據庫宕機時,數據庫頁面只有部分寫入磁盤,導致頁面出現不一致的情況。

1.2 爲什麼會發生頁斷裂

InnoDB中有記錄(Row)被更新時,先將其在Buffer Pool中的page更新,並將這次更新記錄到Redo Log file中,這時候Buffer Pool中的該page就是被標記爲Dirty(髒頁)。在適當的時候(Buffer Pool不夠、Redo不夠,系統閒置等),這些Dirty Page會被Checkpoint刷新到磁盤進行持久化操作。

mysql的一個update需要經歷什麼最終持久化到磁盤?

我們知道,InnoDB的Page Size是16KB,其數據校驗也是針對這16KB來計算的,將數據寫入到磁盤是以Page爲單位進行操作的。我們知道文件系統是以4k爲單位寫入,機械磁盤是以扇區(512字節)爲單位寫入(SSD本質上雖然沒有扇區概念,但爲了兼容機械盤,也搞出了這麼一個512字節扇區這麼一個寫方式),不能保證MySQL數據頁面16KB的一次性原子寫。試想,在某個Dirty Page flush的過程中,發生了系統斷電(或者OS崩潰),16K的數據只有8K被寫到磁盤上,只有一部分寫是成功的,這種現象被稱爲(partial page writes、torn pages、fractured writes)。

查看innoDB的Page Size大小

mysql> show variables like 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.00 sec)

查看OS block Size大小

$ getconf PAGESIZE

4096

1.3 InnoDB使用redo log可以恢復頁斷裂嗎

一旦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後實際上會報錯。

注:mysql的邏輯日誌、物理日誌與物理邏輯日誌

物理邏輯日誌不是完全冪等的,這取決於重做日誌類型,對於INSERT產生的日誌其不是冪等的。

2. double write

2.1 簡述

爲了解決頁斷裂(partial write)問題,InnoDB實現了double write機制。

簡單來說,在寫數據頁之前,先把這個數據頁寫到一個獨立的物理文件位置(ibdata),然後再寫到數據頁。這樣在宕機恢復的時候,如果數據頁損壞,那麼在應用redo log之前,需要通過該頁的副本來還原該頁,然後在進行redo log重做。這就是doublewrite。doublewrite技術帶給innodb存儲引擎的是數據頁的可靠性。

2.2 double write體系結構和工作流程

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刷新(寫出)緩衝區中的數據頁時採用的是一次寫多個頁的方式:

  1. 多個頁就可以先順序寫入到double write buffer,並調用fsync()保證這些數據被刷新到double write磁盤(ibdata)。
  2. 然後數據頁調用fsync()被刷新到實際存儲位置;
  3. 故障恢復時InnoDB檢查double write Buffer與數據頁原存儲位置的內容,若double write頁處於頁斷裂狀態,則簡單的丟棄;若數據頁不一致,則從double write頁還原。

由於double write頁落盤與數據頁落盤在不同的時間點,不會出現double write頁和數據頁同時發生斷裂的情況,因此doublewrite技術可以解決頁斷裂問題,進而保證了重做日誌能順利進行,數據庫能恢復到一致的狀態。

2.3 double write性能損耗

表面看上去,每個頁面都寫了2遍,會非常影響性能。但實際上,由於所寫的頁面會先緩存到內存中,因此每一部分緩存空間在滿了之後纔會真正的寫入文件。並且double write是一個連續的存儲空間,所以硬盤在寫數據的時候是順序寫,而不是隨機寫,這樣性能很高。doublewrite有效利用這個特點,所以降低並不會相差1倍,經過測試,大概5-10%左右。當然,這是針對普通磁盤。對於目前比較流行的SSD來說,隨機寫已經不是問題,性能影響可能更小。

2.4 double write的配置

double write並不是什麼特性或優點,它只是一個被動解決方案而已。這個問題的本質就是磁盤在寫入時,都是以512字節爲單位,不能保證MySQL數據頁面16KB的一次性原子寫,所以纔有可能產生頁面斷裂的問題。而目前有些廠商從硬件驅動層面做了優化,可以保證16KB(或其他配置)數據的原子性寫入。如果真是這樣,那麼兩次寫就完全沒有必要了,取消兩次寫,纔是最終級優化。

mysql的double write默認開啓,參數skip_innodb_doublewrite雖然可以禁止使用doublewrite功能,但還是強烈建議大家使用doublewrite,避免部分寫失效問題。

mysql> show variables like '%double%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| innodb_doublewrite | ON    |
+--------------------+-------+
1 row in set (0.00 sec)

2.5 InnoDB能否通過其他方式解決partial write

其實保證mysql數據頁面(16KB)原子性的寫入到磁盤(每次512字節)中,就可以解決partial write。

  1. 如果系統表空間文件(“ibdata文件”)位於支持原子寫入的Fusion-io設備上,就能避免partial write ;
  2. 阿里雲polardb,在底層分佈式文件系統PolarFS能提供頁大小(如16)KB小的原子寫入,無需double write機制來避免partial write。

推薦閱讀

MySQL InnoDB特性:兩次寫(DoubleWrite)

頁斷裂(partial write)與doublewrite技術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章