您好,登錄后才能下訂單哦!
云妹導讀:
所謂寫確認,是指用戶將數據寫入數據庫之后,數據庫告知用戶寫入成功的一個概念。根據數據庫的特點和配置,可以在不同的寫入程度上,返回給用戶,而這其中,就涉及到了不同的性能、數據安全等級以及數據一致性的內容。
不同的寫入確認級別或配置,是數據庫提供給用戶的一種自我控制的能力,用戶可以針對自身業務的特點、數據管理的需要、性能的考慮、數據一致性以及服務可用性各種因素進行考慮,選擇適合的數據庫配置,來實現自身的需要。
首先介紹幾個重要的概念,這些概念也是數據庫中常識性的知識了,不過是在不同數據庫的不同表述。
這些概念主要涉及到寫確認的兩個重要考量點, 一個是本地數據庫寫操作的不丟失,一個是分布式環境下,數據冗余的一致性。
本地數據庫寫操作是指數據庫在處理用戶的寫操作后,能夠持續化,防止因為意外導致的數據丟失,這個主要涉及到日志,比如MySQL中的redo log和MongoDB中的journal日志。
數據冗余的一致性是指多副本的環境下,比如主從或復制集架構下,數據寫入主節點后,如何實現從節點與主節點的數據一致,而主從之間是以另外一個日志實現數據同步的,比如MySQL的binlog和MongoDB中的oplog日志。
另外防止主節點崩潰,數據未能同步到從節點,導致從節點成為新的主節點后,未同步數據丟失,也是寫確認中重要的內容,即不但同步數據,而且要讓數據安全快速的同步。
MySQL的redo log和MongoDB的journal日志都是數據庫存儲引擎層面的WAL(Write-Ahead Logging)預寫式日志,記錄的是數據的物理修改,是提高數據系統持久性的一種技術。
redo log是MySQL的默認存儲引擎innodb事務日志中的核心日志文件之一,俗稱重做日志,主要用作前滾的數據恢復。
當我們想要修改MySQL數據庫中某一行數據的時候,innodb是把數據從磁盤讀取到內存的緩沖池上進行修改。這個時候數據在內存中被修改,與磁盤中相比就存在了差異,我們稱這種有差異的數據為臟頁。innodb對臟頁的處理不是每次生成臟頁就將臟頁刷新回磁盤,這樣會產生海量的io操作,嚴重影響innodb的處理性能,因此并不是每次有了臟頁都立刻刷新到磁盤中。既然臟頁與磁盤中的數據存在差異,那么如果在這期間數據庫出現故障就會造成數據的丟失。
而redo log就是為了解決這個問題。由于redo log的存在,可以延遲刷新臟頁到磁盤的時間,保障了數據庫性能的情況下提高了數據的安全。雖然增加了redo log刷新的開銷,但是由于redo log采用的順序io,比數據頁的隨機io要快很多,這額外的開銷可接受。
即,數據庫先將數據頁的物理修改情況寫到刷盤較快的redo log文件中,防止數據丟失。一旦發生故障,數據庫重啟恢復的時候,可以先從redo log把未刷新到磁盤的已經提交的物理數據頁恢復回來。
journal是MongoDB存儲引擎層面的概念,MongoDB主要支持的mmapv1、wiredtiger、mongorocks等存儲引擎,都?持配置journal。MongoDB可以基于journal來恢復因為崩潰未及時寫到磁盤的信息。
MongoDB 所有的數據寫?、讀取最終都是調存儲引擎層的接?來存儲、讀取數據,journal 是存儲引擎存儲數據時的一種輔助機制。
在MongoDB的4.0版本以前,用戶可以設置是否開啟journal日志;從4.0版本開始,副本集成員必須開啟journal功能。
以wiredtiger為例,如果不配置journal,寫入wiredtiger的數據,并不會立即持久化存儲;而是每分鐘會做一次全量的checkpoint( storage.syncPeriodSecs配置項,默認為1分鐘),將所有的數據持久化。如果中間出現宕機,那么數據只能恢復到最近的一次checkpoint,這樣最多可能丟掉1分鐘的數據。
所以建議「一定要開啟journal」,開啟journal后,每次寫入會記錄一條操作日志(通過journal可以重新構造出寫入的數據)。這樣即使出現宕機,啟動時 Wiredtiger 會先將數據恢復到最近的一次checkpoint的點,然后重放后續的 journal操作日志來恢復數據。
MySQL的binlog和MongoDB的oplog都是數據庫層面的寫操作對應的邏輯日志,主要用于實現數據在主備之間的同步復制以及增量備份和恢復。
binlog是MySQL數據庫層面的一種二進制日志,不管底層使用的什么存儲引擎,對數據庫的修改都會產生這種日志。binlog記錄操作的方法是邏輯性語句,可以通過設置log-bin=mysql-bin來啟動該功能。
binlog中記錄了有關寫操作的執行時間、操作類型、以及操作的具體內容,比如SQL語句(statement)或每行實際數據的變更(row)。
上圖是MySQL主從之間是如何實現數據復制的,其中的三個重要過程是:
這樣源源不斷的復制,實現了數據在數據庫節點之間的一致。
oplog是MongoDB數據庫層面的概念,在復制集架構下,主備節點之間通過oplog來實現節點間的數據同步。Primary中所有的寫入操作都會記錄到MongoDB Oplog中,然后從庫會來主庫一直拉取Oplog并應用到自己的數據庫中。這里的Oplog是MongoDB local數據庫的一個集合,它是Capped collection,通俗意思就是它是固定大小,循環使用的。
oplog 在 MongoDB 里是一個普通的 capped collection,對于存儲引擎來說,oplog只是一部分普通的數據而已。
只有按復制集架構啟動的節點會自動在local庫中創建oplog.rs的集合。
oplog中記錄了有關寫操作的操作時間、操作類型、以及操作的具體內容,幾乎保留的每行實際數據的變更(在4.0及以后版本中,一個事務中涉及的多個文檔,會寫在一條oplog中)。
上圖是MongoDB主備之間如何實現數據復制的,其中的四個重要過程是:
這樣源源不斷的復制,實現了數據在數據庫節點之間的一致。
另外MongoDB支持鏈式復制,即oplog不一定從Primary中獲取,還可以從其他Secondary獲取。上圖是MongoDB主備之間如何實現數據復制的,其中的四個重要過程是:
這樣源源不斷的復制,實現了數據在數據庫節點之間的一致。
另外MongoDB支持鏈式復制,即oplog不一定從Primary中獲取,還可以從其他Secondary獲取。
journal日志是在wiretiger、mmapV1等存儲引擎層產生,而oplog是MongoDB數據庫的主從復制層面的概念,oplog也與存儲引擎無關;
兩種日志記錄的內容形式不同。MongoDB的oplog是邏輯日志,其記錄的是對應的寫操作的內容。而journal存儲的物理修改;
兩種日志與記錄寫入磁盤的時間點不同。
MongoDB 復制集里寫入一個文檔時,需要修改如下數據
寫確認這個概念其實是來自于MongoDB中的write concern,描述的是MongoDB對一個寫操作的確認(acknowledge)等級。而MySQL中對應的這個概念,可以理解為,用戶在提交(commit)寫操作的時候,需要經過哪些操作之后就會告知用戶提交成功。
在MongoDB中,數據庫支持基于write concern功能使用戶配置靈活的寫入策略,則不同的策略對應不同的數據寫入程度即返回給用戶寫入成功,用戶可以繼續操作下一個寫請求。
write concern
write concern支持3個配置項:
{ w: , j: , wtimeout: }
其中:
副本集下的寫確認
下面以一個副本集架構來描述,一個寫操作的流程,來認識MongoDB下的寫確認。
上面這個寫操作,{w:2},需要至少兩個節點寫成功才可以返回給用戶寫成功;而每個節點的寫入成功可以基于參數{j}來判斷,如果{j:true},則每個節點寫入操作的journal都刷盤才可以;如果{j:false},則寫入操作的journal在緩存中即可以返回成功;
另外,MongoDB的Primary如何知道Secondary是否已經同步成功呢,是基于如下流程:
MySQL數據庫在所謂寫確認或寫成功方面可以通過執行事務的commit提交來體現,提交成功則為寫成功。因此我主要從事務在執行事務以及commit事務的過程中,涉及的redo log、binlog以及兩種日志的刷盤和主從復制的流程來分析MySQL的寫成功相關的設置和問題。
MySQL復制架構
目前MySQL較為流量的版本包括5.5、5.6、5.7、8.0,而8.0版本中使用的Group Replication來實現多節點的數據一致性,這種組復制依靠分布式一致性協議(Paxos協議的變體),實現了分布式下數據的最終一致性。
MySQL中有幾種常見復制機制:
除了組復制,半同步復制技術是性能和安全相對更好的設計,尤其在5.7版本中,優化了之前版本的半同步復制相關的邏輯,因此我們主要以5.7版本來介紹。
MySQL5.6/5.5半同步復制的原理:提交事務的線程會被鎖定,直到至少一個Slave收到這個事務,由于事務在被提交到存儲引擎之后才被發送到Slave上,所以事務的丟失數量可以下降到最多每線程一個。因為事務是在被提交之后才發送給Slave的,當Slave沒有接收成功,并且Master掛了,會導致主從不一致:主有數據,從沒有數據。這個被稱為AFTER_COMMIT。
MySQL5.7在Master事務提交的時間方面做了改進,事務是在提交之前發送給Slave(AFTER_SYNC),當Slave沒有接收成功,并且Master宕機了,不會導致主從不一致,因為此時主還沒有提交,所以主從都沒有數據。
不過假如Slave接收成功,并且Master中的binlog未來得及刷盤并且在存儲引擎提交之前宕機了,那么很明顯這個事務是不成功的,但由于對應的Binlog已經做了Sync操作,從庫已經收到了這些Binlog,并且執行成功,相當于在從庫上多了數據,也算是有問題的,但多了數據,問題一般不算嚴重。此時可能就需要8.0版本中的組復制了。
MySQL寫確認行為
我們以MySQL的5.7版本的半同步復制的主從架構的為例子,來介紹MySQL各個參數對寫確認即commit的不同影響。
上圖中,能夠體現半同步復制(AFTER SYNC)的過程為:
上圖中,能體現redo log和binlog順序一致性的過程為:
下面將把上面介紹的與刷盤有關的配置項引入這整個過程,來看寫操作不同的行為和風險。
注意:以上是在開始內部兩階段提交的流程,即innodb_support_xa=true,這個時候可以通過判斷binlog來恢復會提交的事務,因此innodb_flush_log_at_trx_commit看起來可有可無;如果未開啟內部事務的兩階段提交,則更會復雜,只有innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1的情況下,可以保證已提交事務的安全,其他情況都有可能導致數據丟失或者主從數據不一致的風險。
但是在innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1 的情況下,MySQL的性能相對最低。可以在提高性能的情況下,比如 innodb_flush_log_at_trx_commit = 2 且 sync_binlog = N (N為500 或1000),由于這種情況,redo log和binlog都在系統緩存中,可以使用帶蓄電池后備電源的緩存cache,防止系統斷電異常。
此外,rpl_semi_sync_master_wait_for_slave_count參數是控制同步到多少個節點的,類似MongoDB中write concern中的 w 參數,如果這個參數設置為0(其實不能,最低1),則變為了純粹的異步復制;如果這個參數設置為最大(所有從節點個數),則變為了純粹的同步復制,因此這個地方也可以根據需要來進行調整,來提交數據的安全。
同時,有可能影響同步模式的還包括rpl_semi_sync_master_wait_no_slave參數、影響復制等待超時的參數rpl_semi_sync_master_timeout等。當rpl_semi_sync_master_wait_no_slave為OFF時,只要master發現Rpl_semi_sync_master_clients小于rpl_semi_sync_master_wait_for_slave_count,則master立即轉為異步模式;如果為ON時,如果在事務提交階段(master等待ACK)超時rpl_semi_sync_master_timeout,master會轉為異步模式。
配置比較
其他
雖然MongoDB和MySQL在很多方面可以有類似或相似的設置,但是還是存在一些區別,比如:
本文章所介紹的寫確認的概念,涉及到了MongoDB與MySQL的日志文件(redo log/journal)、同步用日志(binlog/oplog)、刷盤機制和時機、主從同步架構等多個流程和模塊,目的就是實現寫操作的原子性、持久性、分布式環境下的數據一致性等,對數據的性能和安全都有影響,需要根據數據、業務、壓力、安全等客觀因素去調整。
由于涉及的內容非常多,未對所有的情況進行測試驗證,可能有疏漏或錯誤,希望大家不吝賜教。也希望本篇內容對于對MySQL和MongoDB都有興趣的同學可以作為一個總結和參考。
參考資料
高性能MySQL(
https://item.jd.com/11220393.ht
ml)
MongoDB官方手冊(
https://docs.mongodb.com/manual
/)
深入淺出MongoDB復制(
https://mongoing.com/archives/5
200)
mysql基于binlog的復制(
https://blog.csdn.net/u01254801
6/article/details/86584293)
MongoDB journal 與 oplog,究竟誰先寫入?(
https://mongoing.com/archives/3
988)
MySQL5.7新特性--官方高可用方案MGR介紹(
https://www.cnblogs.com/luoahong/ar
ticles/8043035.html)
MongoDB writeConcern原理解析(
https://mongoing.com/archives/2
916)
mysql日志系統之redo log和bin log(
https://www.jianshu.com/p/4bcfffb27
ed5)
MySQL 5.7 半同步復制增強【轉】(
https://www.cnblogs.com/mao3714/p/8
777470.html)
MySQL 中Redo與Binlog順序一致性問題
【轉】(
https://www.cnblogs.com/mao3714/p/8
734838.html)
詳細分析MySQL事務日志(redo log和undo log)(
https://www.cnblogs.com/f-ck-need-u
/archive/2018/05/08/9010872.html)
MySQL 的"雙1設置"-數據安全的關鍵參數(案例分享)(
https://www.cnblogs.com/kevingrace/
p/10441086.html)
rpl_semi_sync_master_wait_no_slave 參數研究實驗(
https://www.cnblogs.com/konggg/p/12
205505.html)
MySQL5.7新特性半同步復制之AFTER_SYNC/AFTER_COMMIT的過程分析和總結(
http://blog.itpub.net/15498/vi
ewspace-2143986/)
以上,Enjoy~
點擊【 閱讀】,可了解更多數據庫相關詳請
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。