日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

導(dǎo)語MySQL Binlog 用于記錄用戶對數(shù)據(jù)庫操作的結(jié)構(gòu)化查詢語言 (Structured Query Language,SQL) 語句信息。是 MySQL 數(shù)據(jù)庫的二進制日志,可以使用 mysqlbin 命令查看二進制日志的內(nèi)容。愛奇藝在會員訂單系統(tǒng)使用到了 MySQL Binlog,用來實現(xiàn)訂單事件驅(qū)動。在使用 Binlog 后在簡化系統(tǒng)設(shè)計的同時幫助系統(tǒng)提升了可用性和數(shù)據(jù)一致性。本文將從實際應(yīng)用角度出發(fā)理解 MySQL 中的相關(guān)技術(shù)原理,從技術(shù)原理和工作實踐相結(jié)合,幫助大家以及在相關(guān)設(shè)計中存在的潛在問題,希望能給大家有所幫助和啟發(fā),共同進步。作者介紹:作者帆叔目前主要負責(zé)愛奇藝會員交易系統(tǒng)的技術(shù)和架構(gòu)工作,專注異步編程、服務(wù)治理、代碼重構(gòu)等領(lǐng)域,熱愛技術(shù),樂于分享。

 

背景

Binlog 是 MySQL 中一個很重要的日志,主要用于 MySQL 主從間的數(shù)據(jù)同步復(fù)制。正是因為 Binlog 的這項功用,它也被用于 MySQL 向其它類型數(shù)據(jù)庫同步數(shù)據(jù),以及業(yè)務(wù)流程的事件驅(qū)動設(shè)計。通過研究分析,我們發(fā)現(xiàn)使用 MySQL Binlog 實現(xiàn)事件驅(qū)動設(shè)計并沒有想象中那么簡單,所以接下來帶大家了解 MySQL 的 Binlog、Redo Log、數(shù)據(jù)更新內(nèi)部流程,并通過對這些技術(shù)原理的介紹,來分析對業(yè)務(wù)流程可能造成的問題,以及如何避免這些問題。希望通過本文的解析,能夠幫助大家了解到 MySQL 的一些原理,從而幫助大家能夠更順利地使用 MySQL 這個流行的數(shù)據(jù)庫技術(shù)。

基于 Binlog 的事件驅(qū)動

首先介紹一下會員訂單系統(tǒng)的設(shè)計,訂單系統(tǒng)直接向 MQ 發(fā)送消息,通過異步消息驅(qū)動后續(xù)業(yè)務(wù)流程,以實現(xiàn)消息驅(qū)動的設(shè)計。大致的業(yè)務(wù)流程示意圖如下:

MySQL Binlog 技術(shù)原理和業(yè)務(wù)應(yīng)用案例分析「轉(zhuǎn)」

 

圖 1:直接發(fā)送消息的訂單事件驅(qū)動

這種設(shè)計需要保證數(shù)據(jù)庫操作和消息操作的數(shù)據(jù)一致性,即數(shù)據(jù)保存和消息發(fā)送要全部成功或者全部失敗。顯然在數(shù)據(jù)保存前和事務(wù)中進行消息發(fā)送都是不合適的。我們是在數(shù)據(jù)更新操作后,數(shù)據(jù)庫事務(wù)外發(fā)送消息。如果數(shù)據(jù)保存成功,但消息發(fā)送失敗,支付系統(tǒng)需要重新通知(上圖步驟 1),直至通知成功。這種設(shè)計雖然實現(xiàn)了功能和對可用性的基本要求,但存在如下缺點:

  1. 業(yè)務(wù)系統(tǒng)直接依賴消息中間件 :消息中間件的故障,不僅會影響支付通知的處理也可能影響業(yè)務(wù)系統(tǒng)上的其它接口。
  2. 業(yè)務(wù)系統(tǒng)必須實現(xiàn)可靠的重試 :不論是請求發(fā)起方還是請求接收方都必須實現(xiàn)可靠重試才能實現(xiàn)最大努力通知的目標(biāo)。
  3. 重試間隔增大會造成業(yè)務(wù)延遲 :隨著重試次數(shù)增加,每次重試的間隔通常也越來越大,這成為 Exponential Backoff(指數(shù)級退避)。這種設(shè)計能夠讓請求接收方的故障處理更加從容,避免因密集重試造成請求接收方服務(wù)難以恢復(fù)。但這樣做可能會使請求接收方在恢復(fù)服務(wù)之后很長時間后才處理完積壓的消息,從而造成業(yè)務(wù)延遲。我們可以采用類似 Hystrix 的自適應(yīng)設(shè)計,在請求接收方服務(wù)恢復(fù)后回到到正常的請求速率。但這樣的設(shè)計顯然會復(fù)雜許多。

為了解決上述問題,簡化技術(shù)架構(gòu),我們采用事件表的設(shè)計思想,將訂單表作為事件表。通過訂閱訂單表的 Binlog,生成訂單事件,驅(qū)動后續(xù)業(yè)務(wù)流程。在系統(tǒng)架構(gòu)上,業(yè)務(wù)系統(tǒng)不用直接依賴消息中間件,只需專注數(shù)據(jù)庫操作。而通過引入一個接收 Binlog 的獨立的系統(tǒng),將 MySQL 數(shù)據(jù)變化轉(zhuǎn)換成業(yè)務(wù)事件驅(qū)動后續(xù)流程。具體流程如下:

MySQL Binlog 技術(shù)原理和業(yè)務(wù)應(yīng)用案例分析「轉(zhuǎn)」

 

圖 2:基于 Binlog 的訂單事件驅(qū)動

暗藏問題

上文提到,雖然基于 Binlog 的訂單事件驅(qū)動設(shè)計存在諸多優(yōu)點,但后來發(fā)現(xiàn)其實暗藏問題。經(jīng)過實驗,我們發(fā)現(xiàn)偶爾會有訂單履約延遲的現(xiàn)象。

在正常流程中,訂單履約服務(wù)收到訂單支付事件后,會檢查訂單狀態(tài),如果此時訂單狀態(tài)為已支付,則進行履約流程的處理。但對于有履約延遲的訂單,訂單履約服務(wù)收到此訂單的支付事件后,查詢數(shù)據(jù)庫發(fā)現(xiàn)此訂單并非支付狀態(tài)。經(jīng)過調(diào)查,我們排除了數(shù)據(jù)并發(fā)覆蓋問題,并且訂單狀態(tài)查詢是發(fā)生在主庫上,也不存在主從同步延遲問題。

那究竟是什么原因?qū)е聵I(yè)務(wù)系統(tǒng)收到根據(jù) Binlog 生成的訂單支付事件后,再查詢主庫得到的訂單數(shù)據(jù)卻是未支付狀態(tài)的?

對于此問題的原因我們先放下不談,先來看看 MySQL 在更新數(shù)據(jù)時的內(nèi)部原理。

MySQL 數(shù)據(jù)更新相關(guān)原理

本節(jié)將向大家介紹 MySQL 數(shù)據(jù)更新相關(guān)原理,以及在這一過程中最重要的兩種日志:Redo Log 和 Binlog。

Redo Log 和 Binlog

先來介紹 Redo Log 和 Binary Log(Binlog):

  • Redo Log :Redo Log 是 InnoDB 存儲引擎提供的一種物理日志結(jié)構(gòu),用來描述對底層數(shù)據(jù)頁操作的具體內(nèi)容,主要用于實現(xiàn) crash-safe,并提升磁盤操作效率。
  • Binlog :Binlog 是 MySQL 本身提供的一種邏輯日志,和具體存儲引擎無關(guān),描述的是數(shù)據(jù)庫所執(zhí)行的 SQL 語句或數(shù)據(jù)變更情況,主要用于數(shù)據(jù)復(fù)制。

InnoDB 引入 Redo Log 的目的在于實現(xiàn) crash-safe 和提升數(shù)據(jù)更新效率。如果 InnoDB 每次數(shù)據(jù)寫操作都要直接持久化到磁盤上的數(shù)據(jù)頁中,那樣會大量增加磁盤隨機 IO 次數(shù)。引入 Redo Log 后,在對數(shù)據(jù)寫操作時,會將部分隨機 IO 寫變?yōu)轫樞驅(qū)憽R驗榇疟P的順序 IO 效率遠高于隨機 IO,因此引入 Redo Log 機制有助于提升更新數(shù)據(jù)時的性能(如何實現(xiàn) crash-safe 將在下一節(jié)介紹)。

下面的表格說明了兩種日志的作用和它們的不同:

Redo LogBinlog日志類型物理日志,即數(shù)據(jù)頁中的真實二級制數(shù)據(jù),恢復(fù)速度快邏輯日志,SQL 語句 (statement) 或數(shù)據(jù)邏輯變化 (row),恢復(fù)速度慢存儲格式基于 InnoDB 數(shù)據(jù)頁格式進行存儲SQL 語句或數(shù)據(jù)變化內(nèi)容用途重做數(shù)據(jù)頁數(shù)據(jù)復(fù)制層級InnoDB 存儲引擎層MySQL Server 層記錄方式循環(huán)寫追加寫

這時問題來了,現(xiàn)在 MySQL 中存在了兩種日志結(jié)構(gòu):Redo Log 和 Binlog。雖然它們的結(jié)構(gòu)和功能有所不同,但卻記錄著相同的數(shù)據(jù)。如何保證這兩種日志數(shù)據(jù)的一致性,以及如何實現(xiàn) crash-safe 呢?這就引出了兩階段提交設(shè)計。

兩階段提交

兩階段提交不是 Redo Log 或 InnoDB 中的設(shè)計,而是 MySQL 服務(wù)器的設(shè)計(但通常說到兩階段提交時都和 Redo Log 放在一起)。因為 MySQL 采用插件化的存儲引擎設(shè)計,事務(wù)提交時,服務(wù)器本身和存儲引擎都需要提交數(shù)據(jù)。所以從 MySQL 服務(wù)器角度看,其本身就面臨著分布式事務(wù)問題。

為解決此問題,MySQL 引入了兩階段提交。在兩階段提交過程中,Redo Log 會有兩次操作:Prepare 和 Commit。而 Binlog 寫操作則夾在 Redo Log 的 Prepare 和 Commit 操作之間。我們可以設(shè)想一下不同失敗場景下兩階段提交的設(shè)計是如何保證數(shù)據(jù)一致的:

  1. Redo Log Prepare 成功,在寫 Binlog 前崩潰:在故障恢復(fù)后事務(wù)就會回滾。這樣 Redo Log 和 Binlog 的內(nèi)容還是一致的。這種情況比較簡單,比較復(fù)雜的是下一種情況,即在寫 Binlog 和 Redo Log Commit 中間崩潰時,MySQL 是如何處理的?
  2. 在寫 Binlog 之后,但 Redo Log 還沒有 Commit 之前崩潰
  • 如果 Redo Log 有 Commit 標(biāo)識,說明 Redo Log 其實已經(jīng) Commit 成功。這時直接提交事務(wù);
  • 如果 Redo Log 沒有 Commit 標(biāo)識,則使用 XID(事務(wù) ID)查詢 Binlog 相應(yīng)日志,并檢查日志的完整。如果 Binlog 是完整的,則提交事務(wù),否則回滾;

如何判斷 Binlog 是否完整?簡單來說 Statement 格式的 Binlog 最后有 Commit,或 Row 格式的 Binlog 有 XID Event,那 Binlog 就是完整的。

MySQL 數(shù)據(jù)更新流程

接下來看一下 MySQL 執(zhí)行器和 InnoDB 存儲引擎在執(zhí)行簡單 update 語句 update t set n = n + 1 where id = 2 時的流程(因為此例只執(zhí)行單條更新語句,所以其自身就是一個事務(wù))。

  1. 執(zhí)行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。如果 ID=2 這一行所在的數(shù)據(jù)頁本來就在內(nèi)存中,就直接返回給執(zhí)行器;否則,需要先從磁盤讀入內(nèi)存,然后再返回。
  2. 執(zhí)行器拿到引擎給的行數(shù)據(jù),把這個值加上 1,比如原來是 N,現(xiàn)在就是 N+1,得到新的一行數(shù)據(jù),再調(diào)用引擎接口寫入這行新數(shù)據(jù)。
  3. 引擎將這行新數(shù)據(jù)更新到內(nèi)存中。然后將對內(nèi)存數(shù)據(jù)頁的更新內(nèi)容記錄在 Redo Log Buffer 中(這里不詳細介紹 Redo Log Buffer。只需知道對 Redo Log 的操作并不會直接寫在文件上,而是先記錄在內(nèi)存中,然后在特定時刻才會寫入磁盤)。此時完成了數(shù)據(jù)更新操作。
  4. 接下來要進行事務(wù)提交的操作。事務(wù)提交時,Redo Log 被標(biāo)記為 Prepare 狀態(tài)。通常此時,Redo Log 會從 Buffer 寫入磁盤(innodb_flush_log_at_trx_commit,值為 1 時,每次提交事務(wù) Redo Log 都會寫入磁盤)。然后 InnoDB 告知執(zhí)行器執(zhí)行完成,可以提交事務(wù)。
  5. 執(zhí)行器生成本次操作的 Binlog,并把 Binlog 寫入磁盤。
  6. 執(zhí)行器調(diào)用引擎的提交事務(wù)接口,引擎把剛剛寫入的 Redo Log 改成提交 Commit 狀態(tài),更新完成。
MySQL Binlog 技術(shù)原理和業(yè)務(wù)應(yīng)用案例分析「轉(zhuǎn)」

 

圖中描述了 update 語句執(zhí)行過程中 MySQL 執(zhí)行器、InnoDB,以及 Binlog、Redo Log 交互過程(圖中深綠底色的是 MySQL 執(zhí)行器負責(zé)的階段,淺綠底色是 InnoDB 負責(zé)的階段)

問題解析

從上面對 MySQL 原理的介紹我們得知,寫 Binlog 發(fā)生在事務(wù)提交階段,但是 MySQL 因為在 Server 層和存儲引擎層都引入了不同的日志結(jié)構(gòu),從而引入了兩階段提交。Binlog 的寫入發(fā)生在存儲引擎真正提交事務(wù)之前,這導(dǎo)致理論上通過 Binlog 同步數(shù)據(jù)的系統(tǒng)(MySQL 從庫、其它數(shù)據(jù)庫或業(yè)務(wù)系統(tǒng))有可能早于 MySQL 主庫使最新提交的數(shù)據(jù)生效。

所以上面提到的訂單履約服務(wù)在收到基于 Binlog 的訂單支付事件后卻查到相應(yīng)訂單是未支付的,原因很可能是訂單履約服務(wù)在查詢數(shù)據(jù)時,訂單支付數(shù)據(jù)更新操作在 MySQL 內(nèi)部尚未徹底完成事務(wù)的提交。

我們通過開發(fā)驗證程序重現(xiàn)了這一現(xiàn)象。驗證程序接收到事務(wù)提交完成后的完整 Binlog 時會再次在 MySQL 主庫上查詢對應(yīng)的記錄,結(jié)果會有一定概覽獲得事務(wù)提交前的數(shù)據(jù)。

另外經(jīng)過了解,也有同行反映遇到過從庫早于主庫看到數(shù)據(jù)提交的問題。

問題的解決方法

在了解問題背后的原因之后,我們需要思考如何解決此問題。目前解決此問題有兩個方法:重試和直接使用 Binlog 數(shù)據(jù)。

重試這種做法簡單粗暴,既然問題原因是 Binlog 早于事務(wù)提交,那等一下再重試查詢自然就解決了。但在實踐中,需要考慮重試的實現(xiàn)方法、以及是否會因為重試過多甚至無限重試導(dǎo)致服務(wù)異常。對于重試的實現(xiàn),可使用的方法有線程 Sleep 大法和消息重投等方式。線程 Sleep 大法通常是不被推薦的,因為它會導(dǎo)致線程利用率降低,甚至導(dǎo)致服務(wù)無法響應(yīng)。但考慮到本次問題出現(xiàn)概率較低,我們認為線程 Sleep 大法是可以使用的,并且此方式簡單易行,可用于問題的快速修復(fù)。

第二種重試方式是消息重投,比如 RocketMQ 中 Consumer 返回
ConsumeConcurrentlyStatus.RECONSUME_LATER 即可觸發(fā)消息重投。但這種重試方法成本較前一種方法高,另外重試間隔也相對較大,對時間敏感的業(yè)務(wù)影響也較大,因此是否采用此方法需從業(yè)務(wù)和技術(shù)兩個角度綜合考慮。

除了考慮用何種方式重試,還要考慮 ABA 問題,即狀態(tài)變化按照 A->B->A 的方式進行。業(yè)務(wù)系統(tǒng)期待的狀態(tài)是 B,但實際可能沒辦法再變成 B 了。因此在用重試解決此問題之前,需要先排除業(yè)務(wù)系統(tǒng)存在 ABA 問題的可能。對于狀態(tài) ABA 問題,可用狀態(tài)機等方式解決,這里不再展開討論。

除了重試,另一種方法就是直接使用 Binlog。因為 Binlog (row 格式) 直接反映了數(shù)據(jù)的變化情況,其中可以記錄事務(wù)提交涉及到的完整數(shù)據(jù),因此可直接用作業(yè)務(wù)處理。這樣還可以降低數(shù)據(jù)庫 QPS。如果是新設(shè)計的系統(tǒng),我認為這樣做法比較理想。但對于已有系統(tǒng),這種方式改動可能較大,是否采用需權(quán)衡成本和收益。

本文轉(zhuǎn)載自公眾號愛奇藝技術(shù)產(chǎn)品團隊(ID:iQIYI-TP)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzI0MjczMjM2NA==&mid=2247486912&idx=2&sn=8aa949ed12d68424b2df3a1016373db7&chksm=e97691e3de0118f5cbf6deeffb88a0b8fcc5c1936c31d4660f16257cec2bc31c77f435ecaa8e&scene=27#wechat_redirect

分享到:
標(biāo)簽:技術(shù) MySQL Binlog
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定