雲妹導讀:
所謂寫確認,是指用戶將數據寫入數據庫以後,數據庫告知用戶寫入成功的一個概念。根據數據庫的特色和配置,能夠在不一樣的寫入程度上,返回給用戶,而這其中,就涉及到了不一樣的性能、數據安全等級以及數據一致性的內容。
不一樣的寫入確認級別或配置,是數據庫提供給用戶的一種自我控制的能力,用戶能夠針對自身業務的特色、數據管理的須要、性能的考慮、數據一致性以及服務可用性各類因素進行考慮,選擇適合的數據庫配置,來實現自身的須要。html
首先介紹幾個重要的概念,這些概念也是數據庫中常識性的知識了,不過是在不一樣數據庫的不一樣表述。mysql
這些概念主要涉及到寫確認的兩個重要考量點,一個是本地數據庫寫操做的不丟失,一個是分佈式環境下,數據冗餘的一致性。sql
本地數據庫寫操做是指數據庫在處理用戶的寫操做後,可以持續化,防止由於意外致使的數據丟失,這個主要涉及到日誌,好比MySQL中的redo log和MongoDB中的journal日誌。mongodb
數據冗餘的一致性是指多副本的環境下,好比主從或複製集架構下,數據寫入主節點後,如何實現從節點與主節點的數據一致,而主從之間是以另一個日誌實現數據同步的,好比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要快不少,這額外的開銷可接受。app
即,數據庫先將數據頁的物理修改狀況寫到刷盤較快的redo log文件中,防止數據丟失。一旦發生故障,數據庫重啓恢復的時候,能夠先從redo log把未刷新到磁盤的已經提交的物理數據頁恢復回來。
journal是MongoDB存儲引擎層面的概念,MongoDB主要支持的mmapv一、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.七、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.html)
MongoDB官方手冊(https://docs.mongodb.com/manual/)
深刻淺出MongoDB複製(https://mongoing.com/archives/5200)
mysql基於binlog的複製(https://blog.csdn.net/u012548016/article/details/86584293)
MongoDB journal 與 oplog,究竟誰先寫入?(https://mongoing.com/archives/3988)
MySQL5.7新特性--官方高可用方案MGR介紹(https://www.cnblogs.com/luoahong/articles/8043035.html)
MongoDB writeConcern原理解析(https://mongoing.com/archives/2916)
mysql日誌系統之redo log和bin log(https://www.jianshu.com/p/4bcfffb27ed5)
MySQL 5.7 半同步複製加強【轉】(https://www.cnblogs.com/mao3714/p/8777470.html)
MySQL 中Redo與Binlog順序一致性問題 【轉】(https://www.cnblogs.com/mao3714/p/8734838.html)
詳細分析MySQL事務日誌(redo log和undo log)(https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html)
MySQL 的"雙1設置"-數據安全的關鍵參數(案例分享)(http://www.javashuo.com/article/p-dlnoxkdg-do.html)
rpl_semi_sync_master_wait_no_slave 參數研究實驗(https://www.cnblogs.com/konggg/p/12205505.html)
MySQL5.7新特性半同步複製之AFTER_SYNC/AFTER_COMMIT的過程分析和總結(http://blog.itpub.net/15498/viewspace-2143986/)
以上,Enjoy~
點擊【閱讀】,可瞭解更多數據庫相關詳請