日志
1、為什么需要 undo log?
(1)實現事務回滾,保障事務的原子性。
事務處理過程中,如果出現了錯誤或者用戶執 行了 ROLLBACK 語句,MySQL 可以利用 undo log 中的歷史數據將數據恢復到事務開始之前的狀態。
undo log 是一種用于撤銷回退的日志。在事務沒提交之前,MySQL 會先記錄更新前的數據到 undo log 日志文件里面,當事務回滾時,可以利用 undo log 來進行回滾。
在插入一條記錄時,要把這條記錄的主鍵值記下來,這樣之后回滾時只需要把這個主鍵值對應的記錄刪掉就好了;
在刪除一條記錄時,要把這條記錄中的內容都記下來,這樣之后回滾時再把由這些內容組成的記錄插入到表中就好了;
在更新一條記錄時,要把被更新的列的舊值記下來,這樣之后回滾時再把這些列更新為舊值就好了。
(2)實現 MVCC(多版本并發控制)關鍵因素之一。
MVCC 是通過 ReadView + undo log 實現的。undo log 為每條記錄保存多份歷史數據,MySQL 在執行快照讀(普通 select 語句)的時候,會根據事務的 Read View 里的信息,順著 undo log 的版本鏈找到滿足其可見性的記錄。
2、為什么需要 Buffer Pool?
MySQL 的數據都是存在磁盤中的,那么我們要更新一條記錄的時候,得先要從磁盤讀取該記錄,然后在內存中修改這條記錄。那修改完這條記錄是選擇直接寫回到磁盤,還是選擇緩存起來呢?
當然是緩存起來好,這樣下次有查詢語句命中了這條記錄,直接讀取緩存中的記錄,就不需要從磁盤獲取數據了。
為此,Innodb 存儲引擎設計了一個緩沖池(Buffer Pool),來提高數據庫的讀寫性能。
當讀取數據時,如果數據存在于 Buffer Pool 中,客戶端就會直接讀取 Buffer Pool 中的數據,否則再去磁盤中讀取。
當修改數據時,如果數據存在于 Buffer Pool 中,那直接修改 Buffer Pool 中數據所在的頁,然后將其頁設置為臟頁(該頁的內存數據和磁盤上的數據已經不一致),為了減少磁盤I/O,不會立即將臟頁寫入磁盤,后續由后臺線程選擇一個合適的時機將臟頁寫入到磁盤。
3、Buffer Pool 緩存什么?
(1)數據頁
(2)索引頁
(3)插入緩存頁
(4)undo頁
(5)自適應哈希索引
(6)鎖信息
開啟事務后,InnoDB 層更新記錄前,首先要記錄相應的 undo log,如果是更新操作,需要把被更新的列的舊值記下來,也就是要生成一條 undo log,undo log 會寫入 Buffer Pool 中的 Undo 頁面。
4、為什么需要 redo log ?
(1)實現事務的持久性,讓 MySQL 有 crash-safe 的能力
保證 MySQL 在任何時間段突然崩潰,重啟后之前已提交的記錄都不會丟失;
Buffer Pool 是提高了讀寫效率沒錯,但是問題來了,Buffer Pool 是基于內存的,而內存總是不可靠,萬一斷電重啟,還沒來得及落盤的臟頁數據就會丟失。
為了防止斷電導致數據丟失的問題,當有一條記錄需要更新的時候,InnoDB 引擎就會先更新內存(同時標記為臟頁),然后將本次對這個頁的修改以 redo log 的形式記錄下來,這個時候更新就算完成了。
后續,InnoDB 引擎會在適當的時候,由后臺線程將緩存在 Buffer Pool 的臟頁刷新到磁盤里,這就是 WAL (Write-Ahead Logging)技術
WAL 技術指的是, MySQL 的寫操作并不是立刻寫到磁盤上,而是先寫日志,然后在合適的時間再寫到磁盤上。
(2)將寫操作從「隨機寫」變成了「順序寫」,提升 MySQL 寫入磁盤的性能。
我們思考一個問題,redo log 要寫到磁盤,數據也要寫磁盤,為什么要多此一舉?
寫入 redo log 的方式使用了追加操作, 所以磁盤操作是順序寫,而寫入數據需要先找到寫入位置,然后才寫到磁盤,所以磁盤操作是隨機寫。
磁盤的「順序寫 」比「隨機寫」 高效的多,因此 redo log 寫入磁盤的開銷更小。
5、什么是 redo log?
redo log 是物理日志,記錄了某個數據頁做了什么修改,比如對 XXX 表空間中的 YYY 數據頁 ZZZ 偏移量的地方做了AAA 更新,每當執行一個事務就會產生這樣的一條或者多條物理日志。
在事務提交時,只要先將 redo log 持久化到磁盤即可,可以不需要等到將緩存在 Buffer Pool 里的臟頁數據持久化到磁盤。
當系統崩潰時,雖然臟頁數據沒有持久化,但是 redo log 已經持久化,接著 MySQL 重啟后,可以根據 redo log 的內容,將所有數據恢復到最新的狀態。
6、產生的 redo log 是直接寫入磁盤的嗎?
實際上, 執行一個事務的過程中,產生的 redo log 也不是直接寫入磁盤的,因為這樣會產生大量的 I/O 操作,而且磁盤的運行速度遠慢于內存。
所以,redo log 也有自己的緩存—— redo log buffer,每當產生一條 redo log 時,會先寫入到 redo log buffer,后續在持久化到磁盤。
redo log buffer 默認大小 16 MB,可以通過 innodb_log_Buffer_size 參數動態的調整大小,增大它的大小可以讓 MySQL 處理「大事務」是不必寫入磁盤,進而提升寫 IO 性能。
7、redo log 什么時候刷盤?
(1)MySQL 正常關閉時;
(2)當 redo log buffer 中記錄的寫入量大于 redo log buffer 內存空間的一半時,會觸發落盤;
(3)InnoDB 的后臺線程每隔 1 秒,將 redo log buffer 持久化到磁盤。
(4)每次事務提交時都將緩存在 redo log buffer 里的 redo log 直接持久化到磁盤(這個策略可由 innodb_flush_log_at_trx_commit 參數控制)
innodb_flush_log_at_trx_commit 參數:
01.當設置該參數為 0 時,表示每次事務提交時 ,還是將 redo log 留在 redo log buffer 中 ,該模式下在事務提交時不會主動觸發寫入磁盤的操作。
02.當設置該參數為 1 時,表示每次事務提交時,都將緩存在 redo log buffer 里的 redo log 直接持久化到磁盤,這樣可以保證 MySQL 異常重啟之后數據不會丟失。
03.當設置該參數為 2 時,表示每次事務提交時,都只是緩存在 redo log buffer 里的 redo log 寫到 redo log 文件,注意寫入到「 redo log 文件」并不意味著寫入到了磁盤,因為操作系統的文件系統中有個 Page Cache,Page Cache 是專門用來緩存文件數據的,所以寫入「 redo log文件」意味著寫入到了操作系統的文件緩存。
innodb_flush_log_at_trx_commit 為 0 和 2 的時候,InnoDB 的后臺線程每隔 1 秒落盤一次redo log
8、redo log 文件寫滿了怎么辦?
默認情況下, InnoDB 存儲引擎有 1 個重做日志文件組( redo log Group),「重做日志文件組」由有 2 個 redo log 文件組成,這兩個 redo 日志的文件名叫 :ib_logfile0 和 ib_logfile1 。
在重做日志組中,每個 redo log File 的大小是固定且一致的,假設每個 redo log File 設置的上限是 1 GB,那么總共就可以記錄 2GB 的操作。
重做日志文件組是以循環寫的方式工作的,從頭開始寫,寫到末尾就又回到開頭,相當于一個環形。
所以 InnoDB 存儲引擎會先寫 ib_logfile0 文件,當 ib_logfile0 文件被寫滿的時候,會切換至 ib_logfile1 文件,當 ib_logfile1 文件也被寫滿時,會切換回 ib_logfile0 文件。
redo log 是循環寫的方式,相當于一個環形,InnoDB 用 write pos 表示 redo log 當前記錄寫到的位置,用 checkpoint 表示當前要擦除的位置:
如果 write pos 追上了 checkpoint,就意味著 redo log 文件滿了,這時 MySQL 不能再執行新的更新操作,也就是說 MySQL 會被阻塞(因此所以針對并發量大的系統,適當設置 redo log 的文件大小非常重要),此時會停下來將 Buffer Pool 中的臟頁刷新到磁盤中,然后標記 redo log 哪些記錄可以被擦除,接著對舊的 redo log 記錄進行擦除,等擦除完舊記錄騰出了空間,checkpoint 就會往后移動(圖中順時針),然后 MySQL 恢復正常運行,繼續執行新的更新操作。
9、redo log和 binlog的區別
(1)適用對象不同:
binlog 是 MySQL 的 Server 層實現的日志,所有存儲引擎都可以使用;
redo log 是 Innodb 存儲引擎實現的日志;
(2)文件格式不同:
binlog 有 3 種格式類型,分別是 STATEMENT(默認格式)、ROW、 MIXEDSTATEMENT:每一條修改數據的 SQL 都會被記錄到 binlog 中,主從復制中 slave 端再根據 SQL 語句重現。但 STATEMENT 有動態函數的問題,比如你用了 uuid 或者 now 這些函數,你在主庫上執行的結果并不是你在從庫執行的結果,這種隨時在變的函數會導致復制的數據不一致;
ROW:記錄行數據最終被修改成什么樣了(這種格式的日志,就不能稱為邏輯日志了),不會出現 STATEMENT 下動態函數的問題。但 ROW 的缺點是每行數據的變化結果都會被記錄,比如執行批量 update 語句,更新多少行數據就會產生多少條記錄,使 binlog 文件過大,而在 STATEMENT 格式下只會記錄一個 update 語句而已;
MIXED:包含了 STATEMENT 和 ROW 模式,它會根據不同的情況自動使用 ROW 模式和 STATEMENT 模式;
redo log是物理日志,記錄的是在某個數據頁做了什么修改,比如對 XXX 表空間中的 YYY 數據頁 ZZZ 偏移量的地方做了AAA 更新;
(3)寫入方式不同
binlog 是追加寫,寫滿一個文件,就創建一個新的文件繼續寫,不會覆蓋以前的日志,保存的是全量的日志。
redo log 是循環寫,日志空間大小是固定,全部寫滿就從頭開始,保存未被刷入磁盤的臟頁日志。
(4)用途不同:
binlog 用于備份恢復、主從復制;
redo log 用于掉電等故障恢復。
10、如果不小心整個數據庫的數據被刪除了,能使用 redo log 文件恢復數據嗎?
不可以使用 redo log 文件恢復,只能使用 binlog 文件恢復。
因為 redo log 文件是循環寫,是會邊寫邊擦除日志的,只記錄未被刷入磁盤的數據的物理日志,已經刷入磁盤的數據都會從 redo log 文件里擦除。
binlog 文件保存的是全量的日志,也就是保存了所有數據變更的情況,理論上只要記錄在 binlog 上的數據,都可以恢復,所以如果不小心整個數據庫的數據被刪除了,得用 binlog 文件恢復數據。
11、主從復制的流程是怎么樣的?
(1)MySQL 主庫在收到客戶端提交事務的請求之后,會先寫入 binlog,再提交事務,更新存儲引擎中的數據,事務提交完成后,返回給客戶端“操作成功”的響應。
(2)從庫會創建一個專門的 I/O 線程,連接主庫的 log dump 線程,來接收主庫的 binlog 日志,再把 binlog 信息寫入 relay log 的中繼日志里,再返回給主庫“復制成功”的響應。
(3)從庫會創建一個用于回放 binlog 的線程,去讀 relay log 中繼日志,然后回放 binlog 更新存儲引擎中的數據,最終實現主從的數據一致性。
12、從庫是不是越多越好?
不是的。
因為從庫數量增加,從庫連接上來的 I/O 線程也比較多,主庫也要創建同樣多的 log dump 線程來處理復制的請求,對主庫資源消耗比較高,同時還受限于主庫的網絡帶寬。
13、MySQL 主從復制還有哪些模型?
(1)同步復制:MySQL 主庫提交事務的線程要等待所有從庫的復制成功響應,才返回客戶端結果。這種方式在實際項目中,基本上沒法用,原因有兩個:一是性能很差,因為要復制到所有節點才返回響應;二是可用性也很差,主庫和所有從庫任何一個數據庫出問題,都會影響業務。
(2)異步復制(默認模型):MySQL 主庫提交事務的線程并不會等待 binlog 同步到各從庫,就返回客戶端結果。這種模式一旦主庫宕機,數據就會發生丟失。
(3)半同步復制:MySQL 5.7 版本之后增加的一種復制方式,介于兩者之間,事務線程不用等待所有的從庫復制成功響應,只要一部分復制成功響應回來就行,比如一主二從的集群,只要數據成功復制到任意一個從庫上,主庫的事務線程就可以返回給客戶端。這種半同步復制的方式,兼顧了異步復制和同步復制的優點,即使出現主庫宕機,至少還有一個從庫有最新的數據,不存在數據丟失的風險。
14、binlog 什么時候刷盤?
binlog寫入流程:事務執行過程中,先把日志寫到 binlog cache(Server 層的 cache),事務提交的時候,再把 binlog cache 寫到 binlog 文件中,并清空 binlog cache。最后由配置決定binlog文件什么時候落到磁盤上。
一個事務的 binlog 是不能被拆開的,因此無論這個事務有多大(比如有很多條語句),也要保證一次性寫入。這是因為有一個線程只能同時有一個事務在執行的設定,所以每當執行一個 begin/start transaction 的時候,就會默認提交上一個事務,這樣如果一個事務的 binlog 被拆開的時候,在備庫執行就會被當做多個事務分段自行,這樣破壞了原子性,是有問題的。
MySQL 給每個線程分配了一片內存用于緩沖 binlog ,該內存叫 binlog cache,參數 binlog_cache_size 用于控制單個線程內 binlog cache 所占內存的大小。如果超過了這個參數規定的大小,就要暫存到磁盤。
雖然每個線程有自己 binlog cache,但是最終都寫到同一個 binlog 文件
圖中的 write,指的就是指把日志寫入到 binlog 文件,但是并沒有把數據持久化到磁盤,因為數據還緩存在文件系統的 page cache 里,write 的寫入速度還是比較快的,因為不涉及磁盤 I/O。
圖中的 fsync,才是將數據持久化到磁盤的操作,這里就會涉及磁盤 I/O,所以頻繁的 fsync 會導致磁盤的 I/O 升高。
MySQL提供一個 sync_binlog 參數來控制數據庫的 binlog 刷到磁盤上的頻率:
01.sync_binlog = 0 的時候,表示每次提交事務都只 write,不 fsync,后續交由操作系統決定何時將數據持久化到磁盤;
02.sync_binlog = 1 的時候,表示每次提交事務都會 write,然后馬上執行 fsync;
03.sync_binlog =N(N>1) 的時候,表示每次提交事務都 write,但累積 N 個事務后才 fsync。
15、一條Update語句的完整過程
(1)執行器負責具體執行,會調用存儲引擎的接口,通過主鍵索引樹搜索獲取 id = 1 這一行記錄(在 buffer pool 中,就直接返回給執行器更新;不在 buffer pool,將數據頁從磁盤讀入到 buffer pool,返回記錄給執行器)
(2)執行器得到聚簇索引記錄后,會看一下更新前的記錄和更新后的記錄是否一樣(一樣就無需走后續流程,不一樣的話就把更新前的記錄和更新后的記錄都當作參數傳給 InnoDB 層,讓 InnoDB 真正的執行更新記錄的操作;)
(3)開啟事務, InnoDB 層更新記錄前,首先要記錄相應的 undo log,因為這是更新操作,需要把被更新的列的舊值記下來,也就是要生成一條 undo log,undo log 會寫入 Buffer Pool 中的 Undo 頁面,不過在內存修改該 Undo 頁面后,需要記錄對應的 redo log。
(4)InnoDB 層開始更新記錄,會先更新內存(同時標記為臟頁),然后將記錄寫到 redo log 里面,這個時候更新就算完成了。為了減少磁盤I/O,不會立即將臟頁寫入磁盤,后續由后臺線程選擇一個合適的時機將臟頁寫入到磁盤。這就是 WAL 技術,MySQL 的寫操作并不是立刻寫到磁盤上,而是先寫 redo 日志,然后在合適的時間再將修改的行數據寫到磁盤上。
(5)至此,一條記錄更新完了。
(6)在一條更新語句執行完成后,然后開始記錄該語句對應的 binlog,此時記錄的 binlog 會被保存到 binlog cache,并沒有刷新到硬盤上的 binlog 文件,在事務提交時才會統一將該事務運行過程中的所有 binlog 刷新到硬盤。
(7)事務提交,剩下的就是「兩階段提交」的事情了
16、為什么需要兩階段提交?
事務提交后,redo log 和 binlog 都要持久化到磁盤,但是這兩個是獨立的邏輯,可能出現半成功的狀態,這樣就造成兩份日志之間的邏輯不一致。
(1)如果在將 redo log 刷入到磁盤之后, MySQL 突然宕機了,而 binlog 還沒有來得及寫入。在主從架構中,binlog 會被復制到從庫,由于 binlog 丟失了某些更新語句,會導致主從數據不一致。
(2)如果在將 binlog 刷入到磁盤之后, MySQL 突然宕機了,而 redo log 還沒有來得及寫入。由于 redo log 還沒寫,崩潰恢復以后這個事務無效,主庫中可能丟失某些更新語句,binlog 會被復制到從庫,從庫執行了這些更新語句,也會導致主從數據不一致。
MySQL 為了避免出現兩份日志之間的邏輯不一致的問題,使用了「兩階段提交」來解決,兩階段提交其實是分布式事務一致性協議,它可以保證多個邏輯操作要不全部成功,要不全部失敗,不會出現半成功的狀態。
17、什么是兩階段提交?
在 MySQL 的 InnoDB 存儲引擎中,開啟 binlog 的情況下,MySQL 會同時維護 binlog 日志與 InnoDB 的 redo log,為了保證這兩個日志的一致性,MySQL 使用了內部 XA 事務,內部 XA 事務由 binlog 作為協調者,存儲引擎是參與者。
事務的提交過程有兩個階段,就是將 redo log 的寫入拆成了兩個步驟:prepare 和 commit,中間再穿插寫入binlog:
(1)prepare 階段:將 XID(內部 XA 事務的 ID) 寫入到 redo log,同時將 redo log 對應的事務狀態設置為 prepare,然后將 redo log 持久化到磁盤(innodb_flush_log_at_trx_commit = 1 的作用);
(2)commit 階段:把 XID 寫入到 binlog,然后將 binlog 持久化到磁盤(sync_binlog = 1 的作用),接著調用引擎的提交事務接口,將 redo log 狀態設置為 commit,此時該狀態并不需要持久化到磁盤,只需要 write 到文件系統的 page cache 中就夠了,因為只要 binlog 寫磁盤成功,就算 redo log 的狀態還是 prepare 也沒有關系,一樣會被認為事務已經執行成功;
18、分析兩階段提交下異常重啟不會導致主從數據不一致。
在 MySQL 重啟后會按順序掃描 redo log 文件,碰到處于 prepare 狀態的 redo log,就拿著 redo log 中的 XID 去 binlog 查看是否存在此 XID:
(1)如果 binlog 中沒有當前內部 XA 事務的 XID,說明 redolog 完成刷盤,但是 binlog 還沒有刷盤,則回滾事務。
(2)如果 binlog 中有當前內部 XA 事務的 XID,說明 redolog 和 binlog 都已經完成了刷盤,則提交事務。
對于處于 prepare 階段的 redo log,即可以提交事務,也可以回滾事務,這取決于是否能在 binlog 中查找到與 redo log 相同的 XID,如果有就提交事務,如果沒有就回滾事務。這樣就可以保證 redo log 和 binlog 這兩份日志的一致性了。
所以說,兩階段提交是以 binlog 寫成功為事務提交成功的標識,因為 binlog 寫成功了,就意味著能在 binlog 中查找到與 redo log 相同的 XID。
19、事務沒提交的時候,redo log 會被持久化到磁盤嗎?這樣會不會有問題?
事務執行中間過程的 redo log 也是直接寫在 redo log buffer 中的,這些緩存在 redo log buffer 里的 redo log 也會被「后臺線程」每隔一秒一起持久化到磁盤。也就是說,事務沒提交的時候,redo log 也是可能被持久化到磁盤的。
如果 mysql 崩潰了,還沒提交事務的 redo log 已經被持久化磁盤了,mysql 重啟后,數據不就不一致了?
這種情況 mysql 重啟會進行回滾操作,因為事務沒提交的時候,binlog 是還沒持久化到磁盤的。所以, redo log 可以在事務沒提交之前持久化到磁盤,但是 binlog 必須在事務提交之后,才可以持久化到磁盤。
20、兩階段提交存在什么問題?
兩階段提交雖然保證了兩個日志文件的數據一致性,但是性能很差,主要有兩個方面的影響:
(1)磁盤 I/O 次數高:對于“雙1”配置,每個事務提交都會進行兩次 fsync(刷盤),一次是 redo log 刷盤,另一次是 binlog 刷盤。
binlog 和 redo log 在內存中都對應的緩存空間,binlog 會緩存在 binlog cache,redo log 會緩存在 redo log buffer,它們持久化到磁盤的時機分別由下面這兩個參數控制。一般我們為了避免日志丟失的風險,會將這兩個參數設置為 1:
01.當 sync_binlog = 1 的時候,表示每次提交事務都會將 binlog cache 里的 binlog 直接持久到磁盤;
02.當 innodb_flush_log_at_trx_commit = 1 時,表示每次事務提交時,都將緩存在 redo log buffer 里的 redo log 直接持久化到磁盤;
(2)鎖競爭激烈:兩階段提交雖然能夠保證「單事務」兩個日志的內容一致,但在「多事務」的情況下,卻不能保證兩者的提交順序一致,因此,在兩階段提交的流程基礎上,還需要加一個鎖來保證提交的原子性,從而保證多事務的情況下,兩個日志的提交順序一致。
在早期的 MySQL 版本中,通過使用 prepare_commit_mutex 鎖來保證事務提交的順序,在一個事務獲取到鎖時才能進入 prepare 階段,一直到 commit 階段結束才能釋放鎖,下個事務才可以繼續進行 prepare 操作。
通過加鎖雖然完美地解決了順序一致性的問題,但在并發量較大的時候,就會導致對鎖的爭用,性能不佳。
21、怎么解決兩階段提交的問題?
MySQL 引入了 binlog 組提交(group commit)機制,當有多個事務提交的時候,會將多個 binlog 刷盤操作合并成一個,從而減少磁盤 I/O 的次數,如果說 10 個事務依次排隊刷盤的時間成本是 10,那么將這 10 個事務一次性一起刷盤的時間成本則近似于 1。
22、詳細說說兩階段提交的流程。
(1)flush 階段
第一個事務會成為 flush 階段的 Leader,此時后面到來的事務都是 Follower獲取隊列中的事務組,由事務組的 Leader 對 rodo log 做一次 write + fsync,即一次將同組事務的 redolog 刷盤:完成了 prepare 階段后,將這一組事務執行過程中產生的 binlog 寫入 binlog 文件(調用 write,不會調用 fsync,所以不會刷盤,binlog 緩存在操作系統的文件系統中)。
(2)sync 階段
一組事務的 binlog 寫入到 binlog 文件后,并不會馬上執行刷盤的操作,而是會等待一段時間,這個等待的時長由 Binlog_group_commit_sync_delay 參數控制,目的是為了組合更多事務的 binlog,然后再一起刷盤在等待的過程中,如果事務的數量提前達到了 Binlog_group_commit_sync_no_delay_count 參數設置的值,就不用繼續等待了,就馬上將 binlog 刷盤binlog_group_commit_sync_delay= N,表示在等待 N 微妙后,直接調用 fsync,將處于文件系統中 page cache 中的 binlog 刷盤,也就是將「 binlog 文件」持久化到磁盤。
binlog_group_commit_sync_no_delay_count = N,表示如果隊列中的事務數達到 N 個,就忽視binlog_group_commit_sync_delay 的設置,直接調用 fsync,將處于文件系統中 page cache 中的 binlog 刷盤。
(3)commit 階段
調用引擎的提交事務接口,將 redo log 狀態設置為 commit。
23、組提交發現MySQL的IO還是很高,有什么辦法優化?
現在我們知道事務在提交的時候,需要將 binlog 和 redo log 持久化到磁盤,那么如果出現 MySQL 磁盤 I/O 很高的現象,我們可以通過控制以下參數,來 “延遲” binlog 和 redo log 刷盤的時機,從而降低磁盤 I/O 的頻率:
(1)設置組提交的兩個參數: binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 參數,延遲 binlog 刷盤的時機,從而減少 binlog 的刷盤次數。
這個方法是基于“額外的故意等待”來實現的,因此可能會增加語句的響應時間,但即使 MySQL 進程中途掛了,也沒有丟失數據的風險,因為 binlog 早被寫入到 page cache 了,只要系統沒有宕機,緩存在 page cache 里的 binlog 就會被持久化到磁盤。
(2)將 sync_binlog 設置為大于 1 的值(比較常見是 100~1000),表示每次提交事務都 write,但累積 N 個事務后才 fsync,相當于延遲了 binlog 刷盤的時機。
這樣做的風險是,主機掉電時會丟 N 個事務的 binlog 日志。
(3)將 innodb_flush_log_at_trx_commit 設置為 2。表示每次事務提交時,都只是緩存在 redo log buffer 里的 redo log 寫到 redo log 文件
注意寫入到「 redo log 文件」并不意味著寫入到了磁盤,因為操作系統的文件系統中有個 Page Cache,專門用來緩存文件數據的,所以寫入「 redo log文件」意味著寫入到了操作系統的文件緩存,然后交由操作系統控制持久化到磁盤的時機。但是這樣做的風險是,主機掉電的時候會丟數據。