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