MongoDB與MySQL關於寫確認的異同

雲妹導讀:
所謂寫確認,是指用戶將數據寫入數據庫以後,數據庫告知用戶寫入成功的一個概念。根據數據庫的特色和配置,能夠在不一樣的寫入程度上,返回給用戶,而這其中,就涉及到了不一樣的性能、數據安全等級以及數據一致性的內容。

不一樣的寫入確認級別或配置,是數據庫提供給用戶的一種自我控制的能力,用戶能夠針對自身業務的特色、數據管理的須要、性能的考慮、數據一致性以及服務可用性各類因素進行考慮,選擇適合的數據庫配置,來實現自身的須要。html

首先介紹幾個重要的概念,這些概念也是數據庫中常識性的知識了,不過是在不一樣數據庫的不一樣表述。mysql

這些概念主要涉及到寫確認的兩個重要考量點,一個是本地數據庫寫操做的不丟失,一個是分佈式環境下,數據冗餘的一致性。sql

本地數據庫寫操做是指數據庫在處理用戶的寫操做後,可以持續化,防止由於意外致使的數據丟失,這個主要涉及到日誌,好比MySQL中的redo log和MongoDB中的journal日誌。mongodb

數據冗餘的一致性是指多副本的環境下,好比主從或複製集架構下,數據寫入主節點後,如何實現從節點與主節點的數據一致,而主從之間是以另一個日誌實現數據同步的,好比MySQL的binlog和MongoDB中的oplog日誌。數據庫

另外防止主節點崩潰,數據未能同步到從節點,致使從節點成爲新的主節點後,未同步數據丟失,也是寫確認中重要的內容,即不但同步數據,並且要讓數據安全快速的同步。緩存

redo/journal

MySQL的redo log和MongoDB的journal日誌都是數據庫存儲引擎層面的WAL(Write-Ahead Logging)預寫式日誌,記錄的是數據的物理修改,是提升數據系統持久性的一種技術。安全

redo log

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

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操做日誌來恢復數據。

binlog/oplog

MySQL的binlog和MongoDB的oplog都是數據庫層面的寫操做對應的邏輯日誌,主要用於實現數據在主備之間的同步複製以及增量備份和恢復。

binlog

binlog是MySQL數據庫層面的一種二進制日誌,無論底層使用的什麼存儲引擎,對數據庫的修改都會產生這種日誌。binlog記錄操做的方法是邏輯性語句,能夠經過設置log-bin=mysql-bin來啓動該功能。

binlog中記錄了有關寫操做的執行時間、操做類型、以及操做的具體內容,好比SQL語句(statement)或每行實際數據的變動(row)。

上圖是MySQL主從之間是如何實現數據複製的,其中的三個重要過程是:

  • 主庫(Master)把數據庫更改記錄到binlog(圖中的Binary Log)中;
  • 備庫(Slave)將主庫上的binlog複製到本身的中繼日誌(Relay log)中;
  • 備庫讀取中繼日誌中的事件,將其重放(Replay)到備庫數據之上。

這樣源源不斷的複製,實現了數據在數據庫節點之間的一致。

oplog

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主備之間如何實現數據複製的,其中的四個重要過程是:

  • 主庫(Primary)把數據庫更改記錄到oplog(圖中的Capped Oplog集合)中;
  • 備庫(Secondary)把主庫上的oplog拉取到本身的回放隊列中(Queue)中;
  • 備庫讀取隊列中的oplog,批量回放(applyOps)到備庫數據中;
  • 再將隊列中的Oplog寫入到備庫中的oplog.rs集合中。

這樣源源不斷的複製,實現了數據在數據庫節點之間的一致。

另外MongoDB支持鏈式複製,即oplog不必定從Primary中獲取,還能夠從其餘Secondary獲取。上圖是MongoDB主備之間如何實現數據複製的,其中的四個重要過程是:

  • 主庫(Primary)把數據庫更改記錄到oplog(圖中的Capped Oplog集合)中;
  • 備庫(Secondary)把主庫上的oplog拉取到本身的回放隊列中(Queue)中;
  • 備庫讀取隊列中的oplog,批量回放(applyOps)到備庫數據中;
  • 再將隊列中的Oplog寫入到備庫中的oplog.rs集合中。

這樣源源不斷的複製,實現了數據在數據庫節點之間的一致。

另外MongoDB支持鏈式複製,即oplog不必定從Primary中獲取,還能夠從其餘Secondary獲取。

redo與binlog

  1. redo log是在innodb存儲引擎層產生,而binlog是MySQL數據庫的上層產生的,而且binlog不只僅針對innodb存儲引擎,MySQL數據庫中的任何存儲引擎對於數據庫的更改都會產生binlog。
  2. 兩種日誌記錄的內容形式不一樣。MySQL的binlog是邏輯日誌,其記錄是對應的SQL語句或行的修改內容。而innodb存儲引擎層面的redo log是物理日誌。
  3. 兩種日誌與記錄寫入磁盤的時間點不一樣,binlog只在事務提交完成後進行一次寫入。而innodb存儲引擎的redo log在事務進行中不斷地被寫入,並日志不是隨事務提交的順序進行寫入的。
  4. binlog僅在事務提交時記錄,而且對於每個事務,僅在事務提交時記錄,而且對於每個事務,僅包含對應事務的一個日誌。而對於innodb存儲引擎的redo log,因爲其記錄是物理操做日誌,所以每一個事務對應多個日誌條目,而且事務的redo log寫入是併發的,並不是在事務提交時寫入,其在文件中記錄的順序並不是是事務開始的順序。
  5. binlog不是循環使用,在寫滿或者重啓以後,會生成新的binlog文件,redo log是循環使用。
  6. binlog能夠做爲恢復數據使用,主從複製搭建,redo log做爲異常宕機或者介質故障後的數據恢復使用。

journal與oplog

journal日誌是在wiretiger、mmapV1等存儲引擎層產生,而oplog是MongoDB數據庫的主從複製層面的概念,oplog也與存儲引擎無關;

兩種日誌記錄的內容形式不一樣。MongoDB的oplog是邏輯日誌,其記錄的是對應的寫操做的內容。而journal存儲的物理修改;

兩種日誌與記錄寫入磁盤的時間點不一樣。

MongoDB 複製集裏寫入一個文檔時,須要修改以下數據

  1. 將文檔數據寫入對應的集合
  2. 更新集合的全部索引信息
  3. 寫入一條oplog用於同步 最終存儲引擎會將全部修改操做應用,並將上述3個操做寫⼊到一條 journal 操做日誌裏。
  4. journal不是循環使用,在寫滿或者重啓以後,會生成新的journal文件,oplog是循環使用;
  5. oplog能夠做爲恢復數據使用,複製集架構,journal做爲一場宕機或者介質故障後的數據恢復使用。

寫確認

寫確認這個概念實際上是來自於MongoDB中的write concern,描述的是MongoDB對一個寫操做的確認(acknowledge)等級。而MySQL中對應的這個概念,能夠理解爲,用戶在提交(commit)寫操做的時候,須要通過哪些操做以後就會告知用戶提交成功。

MongoDB

在MongoDB中,數據庫支持基於write concern功能使用戶配置靈活的寫入策略,則不一樣的策略對應不一樣的數據寫入程度即返回給用戶寫入成功,用戶能夠繼續操做下一個寫請求。

write concern

write concern支持3個配置項:

{ w: , j: , wtimeout: }

其中:

  1. w,該參數要求寫操做已經寫入到個節點才向用戶確認;
  2. {w: 0} 對客戶端的寫入不須要發送任何確認,適用於性能要求高,但不關注正確性的場景;
  3. {w: 1} 默認的writeConcern,數據寫入到Primary就向客戶端發送確認;
  4. {w: "majority"} 數據寫入到副本集大多數成員後向客戶端發送確認,適用於對數據安全性要求比較高的場景,該選項會下降寫入性能;
  5. j,該參數表示是否寫操做要進行journal持久化以後才向用戶確認;
  6. {j: true} 要求primary寫操做進行了journal持久化以後才向用戶確認;
  7. {j: false} 要求寫操做已經在journal緩存中便可向用戶確認;journal後續會將持久化到磁盤,默認是100ms;
  8. wtimeout,該參數表示寫入超時時間,w大於1時有效;當w大於1時,寫操做須要成功寫入若干個節點纔算成功,若是寫入過程當中節點有故障,致使寫操做遲遲不能知足w要求,也就一直不能向用戶返回確認結果,爲了防止這種狀況,用戶能夠設置wtimeout來指定超時時間,寫入過程持續超過該時間仍未結束,則認爲寫入失敗。

副本集下的寫確認

下面以一個副本集架構來描述,一個寫操做的流程,來認識MongoDB下的寫確認。

上面這個寫操做,{w:2},須要至少兩個節點寫成功才能夠返回給用戶寫成功;而每一個節點的寫入成功能夠基於參數{j}來判斷,若是{j:true},則每一個節點寫入操做的journal都刷盤才能夠;若是{j:false},則寫入操做的journal在緩存中便可以返回成功;

另外,MongoDB的Primary如何知道Secondary是否已經同步成功呢,是基於以下流程:

  1. Client向Primary發起請求,指定writeConcern爲{w: "majority"},Primary收到請求,本地寫入並記錄寫請求到oplog,而後等待大多數節點都同步了這條/批oplog(Secondary應用完oplog會向主報告最新進度);
  2. Secondary拉取到Primary上新寫入的oplog,本地重放並記錄oplog。爲了讓Secondary能在第一時間內拉取到主上的oplog,find命令支持一個awaitData的選項,當find沒有任何符合條件的文檔時,並不當即返回,而是等待最多maxTimeMS(默認爲2s)時間看是否有新的符合條件的數據,若是有就返回;因此當新寫入oplog時,備立馬能獲取到新的oplog;
  3. Secondary上有單獨的線程,當oplog的最新時間戳發生更新時,就會向Primary發送replSetUpdatePosition命令更新本身的oplog時間戳;
  4. 當Primary發現有足夠多的節點oplog時間戳已經知足條件了,向客戶端發送確認,這樣,Primary便可知道數據已經同步到了。

MySQL

MySQL數據庫在所謂寫確認或寫成功方面能夠經過執行事務的commit提交來體現,提交成功則爲寫成功。所以我主要從事務在執行事務以及commit事務的過程當中,涉及的redo log、binlog以及兩種日誌的刷盤和主從複製的流程來分析MySQL的寫成功相關的設置和問題。

MySQL複製架構

目前MySQL較爲流量的版本包括5.五、5.六、5.七、8.0,而8.0版本中使用的Group Replication來實現多節點的數據一致性,這種組複製依靠分佈式一致性協議(Paxos協議的變體),實現了分佈式下數據的最終一致性。

MySQL中有幾種常見覆制機制:

  • 同步複製。當主庫提交事務以後,全部的從庫節點必須收到、Replay而且提交這些事務,而後主庫線程才能繼續作後續操做。但缺點是,主庫完成一個事務的時間會被拉長,性能下降。
  • 異步複製。主庫將事務 Binlog 事件寫入到 Binlog文件中,此時主庫只會通知一下 Dump 線程發送這些新的Binlog,而後主庫就會繼續處理提交操做,而此時不會保證這些 Binlog 傳到任何一個從庫節點上。

  • 半同步複製。是介於全同步複製與全異步複製之間的一種,主庫只須要等待至少一個從庫節點收到而且 Flush Binlog 到 Relay Log 文件便可,主庫不須要等待全部從庫給主庫反饋。同時,這裏只是一個收到的反饋,而不是已經徹底完成而且提交的反饋,如此,節省了不少時間。

  • 組複製。由若干個節點共同組成一個複製組,一個事務的提交,必須通過組內大多數節點(N / 2 + 1)決議並經過,才能得以提交。好比由3個節點組成一個複製組,Consensus層爲一致性協議層,在事務提交過程當中,發生組間通信,由2個節點決議(certify)經過這個事務,事務纔可以最終得以提交併響應。

除了組複製,半同步複製技術是性能和安全相對更好的設計,尤爲在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)的過程爲:

  • 第6步,寫binlog;(sync_binlog參數在此起做用)
  • 第7步,同步binlog到備庫的Relay log;(sync_relay_log參數在此起做用)
  • 第8步,返回給主庫ack;
  • 第9步和第10步,提交事務,將redo log中該事務標記爲已提交;(innodb_flush_log_at_trx_commit參數在此起做用)
  • 第11步,返回給用戶寫成功;

上圖中,能體現redo log和binlog順序一致性的過程爲:

  • 第4步,將redo log置爲prepare並刷盤;
  • 第6步,寫入binlog;(sync_binlog參數在此起做用)
  • 第9步和第10步,提交事務,並將redo log置爲提交狀態;

下面將把上面介紹的與刷盤有關的配置項引入這整個過程,來看寫操做不一樣的行爲和風險。

注意:以上是在開始內部兩階段提交的流程,即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在不少方面能夠有相似或類似的設置,可是仍是存在一些區別,好比:

  1. MongoDB的journal中包含了oplog的信息;而binlog和redo log是兩個相對獨立的內容;
  2. MySQL幾乎全部的寫操做都是基於事務來提交的;而MongoDB在4.0開始支持多文檔的事務,單文檔的事務基於內部事務邏輯實現,未直接提供給用戶;
  3. MySQL的寫確認是已commit提交成功爲標誌,MongoDB的普通寫操做是返回寫入成功爲標誌;事務寫操做也是已commit爲標誌;

總結

本文章所介紹的寫確認的概念,涉及到了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~

點擊【閱讀】,可瞭解更多數據庫相關詳請

相關文章
相關標籤/搜索