[TOC]
MySQL作為當下最流行的開源關系型數據庫,有一個很關鍵和基本的能力,就是必須能夠**保證數據不會丟**。那么在這個能力背后,MySQL是如何設計才能保證不管在什么時間崩潰,恢復后都能保證數據不會丟呢?有哪些關鍵技術支撐了這個能力?本文將為我們一一揭曉。
## 一、前言
MySQL 保證數據不會丟的能力主要體現在兩方面:
1. 能夠恢復到任何時間點的狀態;
2. 能夠保證MySQL在任何時間段突然奔潰,重啟后之前提交的記錄都不會丟失;
對于第一點將MySQL恢復到任何時間點的狀態,相信很多人都知道,只要保留有足夠的binlog,就能通過重跑binlog來實現。
對于第二點的能力,也就是本文標題所講的**crash-safe**。即在 InnoDB 存儲引擎中,事務提交過程中任何階段,MySQL突然奔潰,重啟后都能保證事務的完整性,已提交的數據不會丟失,未提交完整的數據會自動進行回滾。這個能力依賴的就是redo log和unod log兩個日志。

因為crash-safe主要體現在事務執行過程中突然奔潰,重啟后能保證事務完整性,所以在講解具體原理之前,先了解下MySQL事務執行有哪些關鍵階段,后面才能依據這幾個階段來進行解析。下面以一條更新語句的執行流程為例,話不多說,直接上圖:
從上圖可以清晰地看出一條更新語句在MySQL中是怎么執行的,簡單進行總結一下:
1. 從內存中找出這條數據記錄,對其進行更新;
2. 將對數據頁的更改記錄到redo log中;
3. 將邏輯操作記錄到binlog中;
4. 對于內存中的數據和日志,都是由后臺線程,當觸發到落盤規則后再異步進行刷盤;
上面演示了一條更新語句的詳細執行過程,接下來咱們通過解答問題,帶著問題來剖析這個crash-safe的設計原理。
## 二、WAL機制
**問題:為什么不直接更改磁盤中的數據,而要在內存中更改,然后還需要寫日志,最后再落盤這么復雜?**
這個問題相信很多同學都能猜出來,MySQL更改數據的時候,之所以不直接寫磁盤文件中的數據,最主要就是性能問題。因為直接寫磁盤文件是隨機寫,開銷大性能低,沒辦法滿足MySQL的性能要求。所以才會設計成先在內存中對數據進行更改,再異步落盤。但是內存總是不可靠,萬一斷電重啟,還沒來得及落盤的內存數據就會丟失,所以還需要加上寫日志這個步驟,萬一斷電重啟,還能通過日志中的記錄進行恢復。
寫日志雖然也是寫磁盤,但是它是順序寫,相比隨機寫開銷更小,能提升語句執行的性能(針對順序寫為什么比隨機寫更快,可以比喻為你有一個本子,按照順序一頁一頁寫肯定比寫一個字都要找到對應頁寫快得多)。
這個技術就是大多數存儲系統基本都會用的**WAL(Write Ahead Log)技術,也稱為日志先行的技術,指的是對數據文件進行修改前,必須將修改先記錄日志。保證了數據一致性和持久性,并且提升語句執行性能。**
## 三、核心日志模塊
**問題:更新SQL語句執行流程中,總共需要寫3個日志,這3個是不是都需要,能不能進行簡化?**
更新SQL執行過程中,總共涉及**MySQL日志模塊其中的三個核心日志,分別是redo log(重做日志)、undo log(回滾日志)、binlog(歸檔日志)。**這里提前預告,crash-safe的能力主要依賴的就是這三大日志。
接下來,針對每個日志將單獨介紹各自的作用,然后再來評估是否能簡化掉。
**1、重做日志 redo log**
redo log也稱為事務日志,由InnoDB存儲引擎層產生。記錄的是數據庫中每個頁的修改,而不是某一行或某幾行修改成怎樣,可以用來恢復提交后的物理數據頁(恢復數據頁,且只能恢復到最后一次提交的位置,因為修改會覆蓋之前的)。
前面提到的WAL技術,redo log就是WAL的典型應用,MySQL在有事務提交對數據進行更改時,只會在內存中修改對應的數據頁和記錄redo log日志,完成后即表示事務提交成功,至于磁盤數據文件的更新則由后臺線程異步處理。由于redo log的加入,保證了MySQL數據一致性和持久性(即使數據刷盤之前MySQL奔潰了,重啟后仍然能通過redo log里的更改記錄進行重放,重新刷盤),此外還能提升語句的執行性能(寫redo log是順序寫,相比于更新數據文件的隨機寫,日志的寫入開銷更小,能顯著提升語句的執行性能,提高并發量),由此可見redo log是必不可少的。
redo log是固定大小的,所以只能循環寫,從頭開始寫,寫到末尾就又回到開頭,相當于一個環形。當日志寫滿了,就需要對舊的記錄進行擦除,但在擦除之前,需要確保這些要被擦除記錄對應在內存中的數據頁都已經刷到磁盤中了。在redo log滿了到擦除舊記錄騰出新空間這段期間,是不能再接收新的更新請求,所以有可能會導致MySQL卡頓。(所以針對并發量大的系統,適當設置redo log的文件大小非常重要!!!)
**2、回滾日志 undo log**
undo log顧名思義,主要就是提供了回滾的作用,但其還有另一個主要作用,就是多個行版本控制(MVCC),保證事務的原子性。在數據修改的流程中,會記錄一條與當前操作相反的邏輯日志到undo log中(可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄),如果因為某些原因導致事務異常失敗了,可以借助該undo log進行回滾,保證事務的完整性,所以undo log也必不可少。
**3、歸檔日志 binlog**
binlog在MySQL的server層產生,不屬于任何引擎,主要記錄用戶對數據庫操作的SQL語句(除了查詢語句)。之所以將binlog稱為歸檔日志,是因為binlog不會像redo log一樣擦掉之前的記錄循環寫,而是一直記錄(超過有效期才會被清理),如果超過單日志的最大值(默認1G,可以通過變量 max\_binlog\_size 設置),則會新起一個文件繼續記錄。但由于日志可能是基于事務來記錄的(如InnoDB表類型),而事務是絕對不可能也不應該跨文件記錄的,如果正好binlog日志文件達到了最大值但事務還沒有提交則不會切換新的文件記錄,而是繼續增大日志,所以 max\_binlog\_size 指定的值和實際的binlog日志大小不一定相等。
正是由于binlog有歸檔的作用,所以binlog主要用作主從同步和數據庫基于時間點的還原。
那么回到剛才的問題,binlog可以簡化掉嗎?這里需要分場景來看:
1. 如果是主從模式下,binlog是必須的,因為從庫的數據同步依賴的就是binlog;
2. 如果是單機模式,并且不考慮數據庫基于時間點的還原,binlog就不是必須,因為有redo log就可以保證crash-safe能力了;但如果萬一需要回滾到某個時間點的狀態,這時候就無能為力,所以建議binlog還是一直開啟;
根據上面對三個日志的詳解,我們可以對這個問題進行解答:在主從模式下,三個日志都是必須的;在單機模式下,binlog可以視情況而定,保險起見最好開啟。
## 四、兩階段提交
**問題:為什么redo log要分兩步寫,中間再穿插寫binlog呢?**
從上面可以看出,因為redo log影響主庫的數據,binlog影響從庫的數據,所以redo log和binlog必須保持一致才能保證主從數據一致,這是前提。
相信很多有過開發經驗的同學都知道分布式事務,這里的redo log和binlog其實就是很典型的分布式事務場景,因為兩者本身就是兩個獨立的個體,要想保持一致,就必須使用分布式事務的解決方案來處理。而將redo log分成了兩步,其實就是使用了兩階段提交協議(Two-phase Commit,2PC)。
下面對更新語句的執行流程進行簡化,看一下MySQL的兩階段提交是如何實現的:

從圖中可看出,事務的提交過程有兩個階段,就是將redo log的寫入拆成了兩個步驟:prepare和commit,中間再穿插寫入binlog。
如果這時候你很疑惑,為什么一定要用兩階段提交呢,如果不用兩階段提交會出現什么情況,比如先寫redo log,再寫binlog或者先寫binlog,再寫redo log不行嗎?下面我們用反證法來進行論證。
我們繼續用update T set c=c+1 where id=2這個例子,假設id=2這一條數據的c初始值為0。那么在redo log寫完,binlog還沒有寫完的時候,MySQL進程異常重啟。由于redo log已經寫完了,系統重啟后會通過redo log將數據恢復回來,所以恢復后這一行c的值是1。但是由于binlog沒寫完就crash了,這時候binlog里面就沒有記錄這個語句。因此,不管是現在的從庫還是之后通過這份binlog還原臨時庫都沒有這一次更新,c的值還是0,與原庫的值不同。
同理,如果先寫binlog,再寫redo log,中途系統crash了,也會導致主從不一致,這里就不再詳述。
所以將redo log分成兩步寫,即兩階段提交,才能保證redo log和binlog內容一致,從而保證主從數據一致。
兩階段提交雖然能夠保證單事務兩個日志的內容一致,但在多事務的情況下,卻不能保證兩者的提交順序一致,比如下面這個例子,假設現在有3個事務同時提交:
```
T1 (--prepare--binlog---------------------commit)
T2 (-----prepare-----binlog----commit)
T3 (--------prepare-------binlog------commit)
解析:
redo log prepare的順序:T1 --》T2 --》T3
binlog的寫入順序:T1 --》 T2 --》T3
redo log commit的順序:T2 --》 T3 --》T1
結論:由于binlog寫入的順序和redo log提交結束的順序不一致,
導致binlog和redo log所記錄的事務提交結束的順序不一樣,最終導致的結果就是主從數據不一致。
```
結論:由于binlog寫入的順序和redo log提交結束的順序不一致,導致binlog和redo log所記錄的事務提交結束的順序不一樣,最終導致的結果就是主從數據不一致。
因此,在兩階段提交的流程基礎上,還需要加一個鎖來保證提交的原子性,從而保證多事務的情況下,兩個日志的提交順序一致。所以在早期的MySQL版本中,通過使用prepare\_commit\_mutex鎖來保證事務提交的順序,在一個事務獲取到鎖時才能進入prepare,一直到commit結束才能釋放鎖,下個事務才可以繼續進行prepare操作。通過加鎖雖然完美地解決了順序一致性的問題,但在并發量較大的時候,就會導致對鎖的爭用,性能不佳。除了鎖的爭用會影響到性能之外,還有一個對性能影響更大的點,就是每個事務提交都會進行兩次fsync(寫磁盤),一次是redo log落盤,另一次是binlog落盤。大家都知道,寫磁盤是昂貴的操作,對于普通磁盤,每秒的QPS大概也就是幾百。
## 五、組提交
**問題:針對通過在兩階段提交中加鎖控制事務提交順序這種實現方式遇到的性能瓶頸問題,有沒有更好的解決方案呢?**
答案自然是有的,在MySQL 5.6 就引入了binlog組提交,即BLGC(Binary Log Group Commit)。binlog組提交的基本思想是,引入隊列機制保證InnoDB commit順序與binlog落盤順序一致,并將事務分組,組內的binlog刷盤動作交給一個事務進行,實現組提交目的。具體如圖:

**第一階段(prepare階段):**
持有prepare\_commit\_mutex,并且write/fsync redo log到磁盤,設置為prepared狀態,完成后就釋放prepare\_commit\_mutex,binlog不作任何操作。
第二個階段(commit階段):這里拆分成了三步,每一步的任務分配給一個專門的線程處理:
1. **Flush Stage(寫入binlog緩存)**
① 持有Lock\_log mutex \[leader持有,follower等待\]
② 獲取隊列中的一組binlog(隊列中的所有事務)
③ 寫入binlog緩存
2. **Sync Stage(將binlog落盤)**
①釋放Lock\_log mutex,持有Lock\_sync mutex\[leader持有,follower等待\]
②將一組binlog落盤(fsync動作,最耗時,假設sync\_binlog為1)。
3. **Commit Stage(InnoDB commit,清楚undo信息)**
①釋放Lock\_sync mutex,持有Lock\_commit mutex\[leader持有,follower等待\]
② 遍歷隊列中的事務,逐一進行InnoDB commit
③ 釋放Lock\_commit mutex
每個Stage都有自己的隊列,隊列中的第一個事務稱為leader,其他事務稱為follower,leader控制著follower的行為。每個隊列各自有mutex保護,隊列之間是順序的。只有flush完成后,才能進入到sync階段的隊列中;sync完成后,才能進入到commit階段的隊列中。但是這三個階段的作業是可以同時并發執行的,即當一組事務在進行commit階段時,其他新事務可以進行flush階段,實現了真正意義上的組提交,大幅度降低磁盤的IOPS消耗。
針對組提交為什么比兩階段提交加鎖性能更好,簡單做個總結:組提交雖然在每個隊列中仍然保留了prepare\_commit\_mutex鎖,但是鎖的粒度變小了,變成了原來兩階段提交的1/4,所以鎖的爭用性也會大大降低;另外,組提交是批量刷盤,相比之前的單條記錄都要刷盤,能大幅度降低磁盤的IO消耗。
## 六、數據恢復流程
**問題:假設事務提交過程中,MySQL進程突然奔潰,重啟后是怎么保證數據不丟失的?**
下圖就是MySQL重啟后,提供服務前會先做的事 -- 恢復數據的流程:

對上圖進行簡單描述就是:奔潰重啟后會檢查redo log中是完整并且處于prepare狀態的事務,然后根據XID(事務ID),從binlog中找到對應的事務,如果找不到,則回滾;找到并且事務完整則重新commit redo log,完成事務的提交。
下面我們根據事務提交流程,在不同的階段時刻,看看MySQL突然奔潰后,按照上述流程是如何恢復數據的。
1. **時刻A**(剛在內存中更改完數據頁,還沒有開始寫redo log的時候奔潰):
因為內存中的臟頁還沒刷盤,也沒有寫redo log和binlog,即這個事務還沒有開始提交,所以奔潰恢復跟該事務沒有關系;
2. **時刻B**(正在寫redo log或者已經寫完redo log并且落盤后,處于prepare狀態,還沒有開始寫binlog的時候奔潰):
恢復后會判斷redo log的事務是不是完整的,如果不是則根據undo log回滾;如果是完整的并且是prepare狀態,則進一步判斷對應的事務binlog是不是完整的,如果不完整則一樣根據undo log進行回滾;
3. **時刻C**(正在寫binlog或者已經寫完binlog并且落盤了,還沒有開始commit redo log的時候奔潰):
恢復后會跟時刻B一樣,先檢查redo log中是完整并且處于prepare狀態的事務,然后判斷對應的事務binlog是不是完整的,如果不完整則一樣根據undo log回滾,完整則重新commit redo log;
4. **時刻D**(正在commit redo log或者事務已經提交完的時候,還沒有反饋成功給客戶端的時候奔潰):
恢復后跟時刻C基本一樣,都會對照redo log和binlog的事務完整性,來確認是回滾還是重新提交。
## 七、總結
至此對MySQL 的crash-safe原理細節就基本講完了,簡單回顧一下:
1. 首先簡單介紹了WAL日志先行技術,包括它的定義、流程和作用。WAL是大部分數據庫系統實現一致性和持久性的通用設計模式。;
2. 接著對MySQL的日志模塊,redo log、undo log、binlog、兩階段提交和組提交都進行了詳細介紹;
3. 最后講解了數據恢復流程,并從不同時刻加以驗證。
轉載:
作者:vivo互聯網技術
鏈接:https://juejin.cn/post/6844904167782236167
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
- 一.JVM
- 1.1 java代碼是怎么運行的
- 1.2 JVM的內存區域
- 1.3 JVM運行時內存
- 1.4 JVM內存分配策略
- 1.5 JVM類加載機制與對象的生命周期
- 1.6 常用的垃圾回收算法
- 1.7 JVM垃圾收集器
- 1.8 CMS垃圾收集器
- 1.9 G1垃圾收集器
- 2.面試相關文章
- 2.1 可能是把Java內存區域講得最清楚的一篇文章
- 2.0 GC調優參數
- 2.1GC排查系列
- 2.2 內存泄漏和內存溢出
- 2.2.3 深入理解JVM-hotspot虛擬機對象探秘
- 1.10 并發的可達性分析相關問題
- 二.Java集合架構
- 1.ArrayList深入源碼分析
- 2.Vector深入源碼分析
- 3.LinkedList深入源碼分析
- 4.HashMap深入源碼分析
- 5.ConcurrentHashMap深入源碼分析
- 6.HashSet,LinkedHashSet 和 LinkedHashMap
- 7.容器中的設計模式
- 8.集合架構之面試指南
- 9.TreeSet和TreeMap
- 三.Java基礎
- 1.基礎概念
- 1.1 Java程序初始化的順序是怎么樣的
- 1.2 Java和C++的區別
- 1.3 反射
- 1.4 注解
- 1.5 泛型
- 1.6 字節與字符的區別以及訪問修飾符
- 1.7 深拷貝與淺拷貝
- 1.8 字符串常量池
- 2.面向對象
- 3.關鍵字
- 4.基本數據類型與運算
- 5.字符串與數組
- 6.異常處理
- 7.Object 通用方法
- 8.Java8
- 8.1 Java 8 Tutorial
- 8.2 Java 8 數據流(Stream)
- 8.3 Java 8 并發教程:線程和執行器
- 8.4 Java 8 并發教程:同步和鎖
- 8.5 Java 8 并發教程:原子變量和 ConcurrentMap
- 8.6 Java 8 API 示例:字符串、數值、算術和文件
- 8.7 在 Java 8 中避免 Null 檢查
- 8.8 使用 Intellij IDEA 解決 Java 8 的數據流問題
- 四.Java 并發編程
- 1.線程的實現/創建
- 2.線程生命周期/狀態轉換
- 3.線程池
- 4.線程中的協作、中斷
- 5.Java鎖
- 5.1 樂觀鎖、悲觀鎖和自旋鎖
- 5.2 Synchronized
- 5.3 ReentrantLock
- 5.4 公平鎖和非公平鎖
- 5.3.1 說說ReentrantLock的實現原理,以及ReentrantLock的核心源碼是如何實現的?
- 5.5 鎖優化和升級
- 6.多線程的上下文切換
- 7.死鎖的產生和解決
- 8.J.U.C(java.util.concurrent)
- 0.簡化版(快速復習用)
- 9.鎖優化
- 10.Java 內存模型(JMM)
- 11.ThreadLocal詳解
- 12 CAS
- 13.AQS
- 0.ArrayBlockingQueue和LinkedBlockingQueue的實現原理
- 1.DelayQueue的實現原理
- 14.Thread.join()實現原理
- 15.PriorityQueue 的特性和原理
- 16.CyclicBarrier的實際使用場景
- 五.Java I/O NIO
- 1.I/O模型簡述
- 2.Java NIO之緩沖區
- 3.JAVA NIO之文件通道
- 4.Java NIO之套接字通道
- 5.Java NIO之選擇器
- 6.基于 Java NIO 實現簡單的 HTTP 服務器
- 7.BIO-NIO-AIO
- 8.netty(一)
- 9.NIO面試題
- 六.Java設計模式
- 1.單例模式
- 2.策略模式
- 3.模板方法
- 4.適配器模式
- 5.簡單工廠
- 6.門面模式
- 7.代理模式
- 七.數據結構和算法
- 1.什么是紅黑樹
- 2.二叉樹
- 2.1 二叉樹的前序、中序、后序遍歷
- 3.排序算法匯總
- 4.java實現鏈表及鏈表的重用操作
- 4.1算法題-鏈表反轉
- 5.圖的概述
- 6.常見的幾道字符串算法題
- 7.幾道常見的鏈表算法題
- 8.leetcode常見算法題1
- 9.LRU緩存策略
- 10.二進制及位運算
- 10.1.二進制和十進制轉換
- 10.2.位運算
- 11.常見鏈表算法題
- 12.算法好文推薦
- 13.跳表
- 八.Spring 全家桶
- 1.Spring IOC
- 2.Spring AOP
- 3.Spring 事務管理
- 4.SpringMVC 運行流程和手動實現
- 0.Spring 核心技術
- 5.spring如何解決循環依賴問題
- 6.springboot自動裝配原理
- 7.Spring中的循環依賴解決機制中,為什么要三級緩存,用二級緩存不夠嗎
- 8.beanFactory和factoryBean有什么區別
- 九.數據庫
- 1.mybatis
- 1.1 MyBatis-# 與 $ 區別以及 sql 預編譯
- Mybatis系列1-Configuration
- Mybatis系列2-SQL執行過程
- Mybatis系列3-之SqlSession
- Mybatis系列4-之Executor
- Mybatis系列5-StatementHandler
- Mybatis系列6-MappedStatement
- Mybatis系列7-參數設置揭秘(ParameterHandler)
- Mybatis系列8-緩存機制
- 2.淺談聚簇索引和非聚簇索引的區別
- 3.mysql 證明為什么用limit時,offset很大會影響性能
- 4.MySQL中的索引
- 5.數據庫索引2
- 6.面試題收集
- 7.MySQL行鎖、表鎖、間隙鎖詳解
- 8.數據庫MVCC詳解
- 9.一條SQL查詢語句是如何執行的
- 10.MySQL 的 crash-safe 原理解析
- 11.MySQL 性能優化神器 Explain 使用分析
- 12.mysql中,一條update語句執行的過程是怎么樣的?期間用到了mysql的哪些log,分別有什么作用
- 十.Redis
- 0.快速復習回顧Redis
- 1.通俗易懂的Redis數據結構基礎教程
- 2.分布式鎖(一)
- 3.分布式鎖(二)
- 4.延時隊列
- 5.位圖Bitmaps
- 6.Bitmaps(位圖)的使用
- 7.Scan
- 8.redis緩存雪崩、緩存擊穿、緩存穿透
- 9.Redis為什么是單線程、及高并發快的3大原因詳解
- 10.布隆過濾器你值得擁有的開發利器
- 11.Redis哨兵、復制、集群的設計原理與區別
- 12.redis的IO多路復用
- 13.相關redis面試題
- 14.redis集群
- 十一.中間件
- 1.RabbitMQ
- 1.1 RabbitMQ實戰,hello world
- 1.2 RabbitMQ 實戰,工作隊列
- 1.3 RabbitMQ 實戰, 發布訂閱
- 1.4 RabbitMQ 實戰,路由
- 1.5 RabbitMQ 實戰,主題
- 1.6 Spring AMQP 的 AMQP 抽象
- 1.7 Spring AMQP 實戰 – 整合 RabbitMQ 發送郵件
- 1.8 RabbitMQ 的消息持久化與 Spring AMQP 的實現剖析
- 1.9 RabbitMQ必備核心知識
- 2.RocketMQ 的幾個簡單問題與答案
- 2.Kafka
- 2.1 kafka 基礎概念和術語
- 2.2 Kafka的重平衡(Rebalance)
- 2.3.kafka日志機制
- 2.4 kafka是pull還是push的方式傳遞消息的?
- 2.5 Kafka的數據處理流程
- 2.6 Kafka的腦裂預防和處理機制
- 2.7 Kafka中partition副本的Leader選舉機制
- 2.8 如果Leader掛了的時候,follower沒來得及同步,是否會出現數據不一致
- 2.9 kafka的partition副本是否會出現腦裂情況
- 十二.Zookeeper
- 0.什么是Zookeeper(漫畫)
- 1.使用docker安裝Zookeeper偽集群
- 3.ZooKeeper-Plus
- 4.zk實現分布式鎖
- 5.ZooKeeper之Watcher機制
- 6.Zookeeper之選舉及數據一致性
- 十三.計算機網絡
- 1.進制轉換:二進制、八進制、十六進制、十進制之間的轉換
- 2.位運算
- 3.計算機網絡面試題匯總1
- 十四.Docker
- 100.面試題收集合集
- 1.美團面試常見問題總結
- 2.b站部分面試題
- 3.比心面試題
- 4.騰訊面試題
- 5.哈羅部分面試
- 6.筆記
- 十五.Storm
- 1.Storm和流處理簡介
- 2.Storm 核心概念詳解
- 3.Storm 單機版本環境搭建
- 4.Storm 集群環境搭建
- 5.Storm 編程模型詳解
- 6.Storm 項目三種打包方式對比分析
- 7.Storm 集成 Redis 詳解
- 8.Storm 集成 HDFS 和 HBase
- 9.Storm 集成 Kafka
- 十六.Elasticsearch
- 1.初識ElasticSearch
- 2.文檔基本CRUD、集群健康檢查
- 3.shard&replica
- 4.document核心元數據解析及ES的并發控制
- 5.document的批量操作及數據路由原理
- 6.倒排索引
- 十七.分布式相關
- 1.分布式事務解決方案一網打盡
- 2.關于xxx怎么保證高可用的問題
- 3.一致性hash原理與實現
- 4.微服務注冊中心 Nacos 比 Eureka的優勢
- 5.Raft 協議算法
- 6.為什么微服務架構中需要網關
- 0.CAP與BASE理論
- 十八.Dubbo
- 1.快速掌握Dubbo常規應用
- 2.Dubbo應用進階
- 3.Dubbo調用模塊詳解
- 4.Dubbo調用模塊源碼分析
- 6.Dubbo協議模塊