亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

從一個線上問題分析binlog與內部XA事務提交過程

發布時間:2020-07-25 15:45:07 來源:網絡 閱讀:3889 作者:AIOPS_DBA 欄目:MySQL數據庫

1. 問題
業務上新增一條訂單記錄,用戶接收到BinLake拉取的MySQL從庫數據消息后,馬上根據消息內的訂單號去查詢同一個MySQL從庫,發現有些時候無法查到該條數據,等待大約500ms~1000ms后再去查詢數據庫,可以查詢到該條數據。
注: BinLake為京東商城數據庫技術部自研的一套訂閱和消費MySQL數據庫binlog的組件,本例所描述的問題是業務方希望根據訂閱的binlog來獲取實時訂單等業務消息。
2. Binlog與內部XA
2.1. XA的概念
XA(分布式事務)規范主要定義了(全局)事務管理器(TM: Transaction Manager)和(局部)資源管理器(RM: Resource Manager)之間的接口。XA為了實現分布式事務,將事務的提交分成了兩個階段:也就是2PC (tow phase commit),XA協議就是通過將事務的提交分為兩個階段來實現分布式事務。
兩階段
1)prepare 階段
事務管理器向所有涉及到的數據庫服務器發出prepare"準備提交"請求,數據庫收到請求后執行數據修改和日志記錄等處理,處理完成后只是把事務的狀態改成"可以提交",然后把結果返回給事務管理器。即:為prepare階段,TM向RM發出prepare指令,RM進行操作,然后返回成功與否的信息給TM。
2)commit 階段
事務管理器收到回應后進入第二階段,如果在第一階段內有任何一個數據庫的操作發生了錯誤,或者事務管理器收不到某個數據庫的回應,則認為事務失敗,回撤所有數據庫的事務。數據庫服務器收不到第二階段的確認提交請求,也會把"可以提交"的事務回撤。如果第一階段中所有數據庫都提交成功,那么事務管理器向數據庫服務器發出"確認提交"請求,數據庫服務器把事務的"可以提交"狀態改為"提交完成"狀態,然后返回應答。即:為事務提交或者回滾階段,如果TM收到所有RM的成功消息,則TM向RM發出提交指令;不然則發出回滾指令。
外部與內部XA
MySQL中的XA實現分為:外部XA和內部XA。前者是指我們通常意義上的分布式事務實現;后者是指單臺MySQL服務器中,Server層作為TM(事務協調者,通常由binlog模塊擔當),而服務器中的多個數據庫實例作為RM,而進行的一種分布式事務,也就是MySQL跨庫事務;也就是一個事務涉及到同一條MySQL服務器中的兩個innodb數據庫(目前似乎只有innodb支持XA)。內部XA也可以用來保證redo和binlog的一致性問題。
2.2. redo與binlog的一致性問題
我們MySQL為了兼容其它非事務引擎的復制,在server層面引入了 binlog, 它可以記錄所有引擎中的修改操作,因而可以對所有的引擎使用復制功能; 然而這種情況會導致redo log與binlog的一致性問題;MySQL通過內部XA機制解決這種一致性的問題。
第一階段:InnoDB prepare, write/sync redo log;binlog不作任何操作;
第二階段:包含兩步,1> write/sync Binlog; 2> InnoDB commit (commit in memory);
當然在5.6之后引入了組提交的概念,可以在IO性能上進行一些提升,但總體的執行順序不會改變。
當第二階段的第1步執行完成之后,binlog已經寫入,MySQL會認為事務已經提交并持久化了(在這一步binlog就已經ready并且可以發送給訂閱者了)。在這個時刻,就算數據庫發生了崩潰,那么重啟MySQL之后依然能正確恢復該事務。在這一步之前包含這一步任何操作的失敗都會引起事務的rollback。
第二階段的第2大部分都是內存操作,比如釋放鎖,釋放mvcc相關的read view等等。MySQL認為這一步不會發生任何錯誤,一旦發生了錯誤那就是數據庫的崩潰,MySQL自身無法處理。這個階段沒有任何導致事務rollback的邏輯。在程序運行層面,只有這一步完成之后,事務導致變更才能通過API或者客戶端查詢體現出來。
下面的一張圖,說明了MySQL在何時會將binlog發送給訂閱者。
從一個線上問題分析binlog與內部XA事務提交過程
理論上來說,也可以在commit階段完成之后再將binlog發送給訂閱者,但這樣會增大主從延遲的風險。
3. 相關代碼

  1. int MYSQL_BIN_LOG::ordered_commit(THD *thd, bool all, bool skip_commit) {
  2. .....
  3. //進入flush stage,
  4. change_stage(thd, Stage_manager::FLUSH_STAGE, thd, NULL, &LOCK_log);
  5. ....
  6. //通知底層存儲引擎日志刷盤
  7. process_flush_stage_queue(&total_bytes, &do_rotate, &wait_queue);
  8. .....
  9. //將各個線程的binlog從cache寫到文件中
  10. flush_cache_to_file(&flush_end_pos);
  11. ....
  12. //進入到Sync stage
  13. change_stage(thd, Stage_manager::SYNC_STAGE, wait_queue, &LOCK_log,
  14. &LOCK_sync));
  15. //binlog fsync落盤
  16. sync_binlog_file(false)
  17. //通知binlog發送線程,有新的binlog落盤可以發送到訂閱者了
  18. update_binlog_end_pos(tmp_thd->get_trans_pos());
  19. //進入commit state
  20. change_stage(thd, Stage_manager::COMMIT_STAGE, final_queue,
  21. leave_mutex_before_commit_stage, &LOCK_commit);
  22. ....
  23. //事務狀態提交
  24. process_commit_stage_queue(thd, commit_queue);
  25. ....
    }

其中,在update_binlog_end_pos之后,binlog發送線程就已經可以讀取最新的binlog發送給訂閱者了。當訂閱者收到這些binlog之后如果process_commit_stage_queue因為系統調度等原因還未執行完成,那么訂閱者碰巧在此時發起問題中所描述的查詢,就會發生查詢不到的情況。
下面我們看一下process_commit_stage_queue都做了什么。
在process_commit_stage_queue會分別調用到binlog的commit方法binlog_commit和innodb的commit函數trx_commit_in_memory。

  1. static int binlog_commit(handlerton , THD , bool) {
  2. DBUG_ENTER("binlog_commit");
  3. /*
  4. Nothing to do (any more) on commit.
  5. */
  6. DBUG_RETURN(0);
  7. }
    在binlog_commit中什么也不做,因為跟binlog有關的操作前面都已經做完了。
    最后看一下存儲引擎innodb的trx_commit_in_memory都干了什么。
  8. static void trx_commit_in_memory(
  9. trx_t trx, /!< in/out: transaction */
  10. const mtr_t mtr, /!< in: mini-transaction of
  11. trx_write_serialisation_history(), or NULL if
  12. the transaction did not modify anything */
  13. bool serialised)
  14. /*!< in: true if serialisation log was
  15. written */
  16. {
  17. ....
  18. //釋放鎖
  19. lock_trx_release_locks(trx);
  20. ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
  21. .....
  22. //釋放mvcc相關的read view
  23. if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
  24. MONITOR_INC(MONITOR_TRX_RO_COMMIT);
  25. if (trx->read_view != NULL) {
  26. trx_sys->mvcc->view_close(trx->read_view, false);
  27. }
  28. } else {
  29. ut_ad(trx->id > 0);
  30. MONITOR_INC(MONITOR_TRX_RW_COMMIT);
  31. }
  32. }
  33. ....
  34. //清理insert操作相關的undo log(注意,此時只有insert的undo需要清理)
  35. if (mtr != NULL) {
  36. if (trx->rsegs.m_redo.insert_undo != NULL) {
  37. trx_undo_insert_cleanup(&trx->rsegs.m_redo, false);
  38. }
  39. if (trx->rsegs.m_noredo.insert_undo != NULL) {
  40. trx_undo_insert_cleanup(&trx->rsegs.m_noredo, true);
  41. }
  42. }

這一步完成之后,在運行時刻事務的變更才能被查詢到。但需要記住,MySQL在binlog落盤成功后就認為事務的持久化已經完成。
30. 總結
在binlog落盤之后,MySQL就會認為事務的持久化已經完成(在這個時刻之后,就算數據庫發生了崩潰都可以在重啟后正確的恢復該事務)。但是該事務產生的數據變更被別的客戶端查詢出來還需要在commit全部完成之后。MySQL會在binlog落盤之后會立即將新增的binlog發送給訂閱者以盡可能的降低主從延遲。但由于多線程時序等原因,當訂閱者在收到該binlog之后立即發起一個查詢操作,可能不會查詢到任何該事務產生的數據變更(因為此時該事務所處線程可能尚未完成最后的commit步驟)。
如果應用需要根據binlog作為一些業務邏輯的觸發點,還是需要考慮引入一些延時重試機制或者重新考慮合適的實現架構。

本文由京東商城數據庫技術部王治提供。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

邵阳县| 灵石县| 古丈县| 江孜县| 西峡县| 岳西县| 连城县| 大洼县| 洛隆县| 信丰县| 大安市| 武汉市| 临泽县| 二手房| 平乐县| 日喀则市| 屏南县| 库尔勒市| 汶上县| 民乐县| 琼结县| 天峻县| 澄城县| 新宾| 健康| 嘉义县| 三河市| 巴楚县| 都兰县| 巴塘县| 东莞市| 内乡县| 虹口区| 家居| 玉龙| 揭阳市| 融水| 徐闻县| 准格尔旗| 洛扎县| 铁岭县|