[TOC]
今天我想和大家一起盤一盤分布式事務,會介紹常見的分布式事務實現方案和其優缺點以及適用的場景,并會帶出它們的一些變體實現。
還會捎帶一下分布式數據庫對 2PC 的改進模型,看看分布式數據庫是如何做的。
然后再分析一波分布式事務框架 Seata 的具體實現,看看分布式事務究竟是如何落地的,畢竟協議要落地才是有用的。

首先我們來提一下事務和分布式事務是什么。
## 事務
事務的 ACID 想必大家都熟知,這其實是嚴格意義上的定義,指的是事務的實現必須具備原子性、一致性、隔離性和持久性。
不過嚴格意義上的事務很難達到,像我們熟知的數據庫就有各種隔離級別,隔離級別越高性能越低,所以**往往我們都會從中找到屬于自己的平衡,不會遵循嚴格意義上的事務**。
并且在我們平日的談論中,所謂的事務往往簡單的指代一系列的操作全部執行成功,或者全部失敗,不會出現一些成功一些失敗的情形。
清晰了平日我們對事務的定義之后,再來看看什么是分布式事務。
## 分布式事務
由于互聯網的快速發展,以往的單體架構頂不住這么多的需求,這么復雜的業務,這么大的流量。
單體架構的優勢在于前期快速搭建、快速上線,并且方法和模塊之間都是內部調用,沒有網絡的開銷更加的高效。
**從某方面來說部署也方便**,畢竟就一個包,扔上去。
不過隨著企業的發展,業務的復雜度越來越高,內部耦合極其嚴重,導致牽一發而動全身,開發不易,測試不易。
并且無法根據熱點服務進行動態的伸縮,比如商品服務訪問量特別大,如果是單體架構的話我們只能把整個應用復制多份集群部署,浪費資源。
因此拆分勢在必行,微服務架構就這么來了。

拆分之后服務之間的邊界就清晰了,每個服務都能獨立地運行,獨立地部署,所以能以服務級別彈性伸縮了。
服務之間的本地調用變成了遠程調用,鏈路更長了,一次調用的耗時更長了,但是總體的吞吐量更大了。
不過拆分之后還會引入其他復雜度,比如服務鏈路的監控、整體的監控、容錯措施、彈性伸縮等等運維監控的問題,還有像分布式事務、分布式鎖跟業務息息相關的問題等。
**往往解決了一個痛點又會引入別的痛點,所以架構的演進都是權衡的結果**,就看你們的系統更能忍受哪種痛點了。
而今天我們談及的就是分布式事務這個痛點。
**分布式事務是由多個本地事務組成的**,分布式事務跨越了多設備,之間又經歷的復雜的網絡,可想而知想要實現嚴格的事務道路阻且長。
單機版事務都不會嚴格遵守事務的嚴格實現,更別說分布式事務了,所以在現實情況下我們只能實現殘缺版的事務。
在明確了事務和分布式事務之后,我們就先來看看常見的分布式事務方案:2PC、3PC、TCC、本地消息、事務消息。
## 2PC
2PC,Two-phase commit protocol,即兩階段提交協議。
它引入了一個事務協調者角色,來管理各個參與者(就是各數據庫資源)。
整體分為兩個階段,分別是準備階段和提交/回滾階段。
我們先來看看第一個階段,即準備階段。

由事務協調者給每個參與者發送準備命令,每個參與者收到命令之后會執行相關事務操作,你可以認為除了事務的提交啥都做了。
然后每個參與者會返回響應告知協調者自己是否準備成功。
**協調者收到每個參與者的響應之后就進入第二階段**,根據收集的響應,如果有一個參與者響應準備失敗那么就向所有參與者發送回滾命令,反之發送提交命令。

這個協議其實很符合正常的思維,就像我們大學上課點名的時候,其實老師就是協調者的角色,我們都是參與者。
老師一個一個的點名,我們一個一個的喊到,最后老師收到所有同學的到之后就開始了今天的講課。
而和點名有所不同的是,**老師發現某幾個學生不在還是能繼續上課,而我們的事務可不允許這樣**。
事務協調者在第一階段未收到個別參與者的響應,則等待一定時間就會認為事務失敗,會發送回滾命令,所以在 2PC 中事務協調者有超時機制。
我們再來分析一下 2PC 的優缺點。
**2PC 的優點**是能利用數據庫自身的功能進行本地事務的提交和回滾,也就是說提交和回滾實際操作不需要我們實現,不侵入業務邏輯由數據庫完成,在之后講解 TCC 之后相信大家對這點會有所體會。
**2PC 主要有三大缺點**:同步阻塞、單點故障和數據不一致問題。

### 同步阻塞
可以看到在第一階段執行了準備命令后,我們**每個本地資源都處于鎖定狀態**,因為除了事務的提交之外啥都做了。
所以這時候如果本地的其他請求要訪問同一個資源,比如要修改商品表 id 等于 100 的那條數據,那么此時是被阻塞住的,必須等待前面事務的完結,收到提交/回滾命令執行完釋放資源后,這個請求才能得以繼續。
所以假設這個分布式事務涉及到很多參與者,然后有些參與者處理又特別復雜,特別慢,那么那些處理快的節點也得等著,所以說效率有點低。
### 單點故障
可以看到這個**單點就是協調者,如果協調者掛了整個事務就執行不下去了**。
如果協調者在發送準備命令前掛了還行,畢竟每個資源都還未執行命令,那么資源是沒被鎖定的。
可怕的是在發送完準備命令之后掛了,這時候每個本地資源都執行完處于鎖定狀態了,都杵著了,這就很僵硬了,如果是某個熱點資源都阻塞了,這估計就要GG了。

### 數據不一致問題
因為協調者和參與者之間的交流是經過網絡的,而網絡有時候就會抽風的或者發生局部網絡異常。
那么就有可能導致某些參與者無法收到協調者的請求,而某些收到了。比如是提交請求,然后那些收到命令的參與者就提交事務了,此時就產生了數據不一致的問題。
### 小結一下 2PC
至此我們來先小結一些 2PC ,它是一個同步阻塞的強一致性兩階段提交協議,分別是準備階段和提交/回滾階段。
2PC 的優勢在于對業務沒有侵入,可以利用數據庫自身機制來進行事務的提交和回滾。
它的缺點:是一個同步阻塞協議,會導致高延遲和性能的下降,并且存在協調者單點故障問題,極端情況下會有數據不一致的問題。
**當然這只是協議,具體的落地還是可以變通了**,比如協調者單點問題,我就搞個主從來實現協調者,對吧。
## 分布式數據庫的 2PC 改進模型
可能有些人對分布式數據庫不熟悉,沒有關系,我們主要學的是思想,看看人家的思路。
我簡單的講下 Percolator 模型,**它是基于分布式存儲系統 BigTable 建立的模型**,BigTable 是啥也不清楚的同學沒有關系影響不大。
還是拿轉賬的例子來說,我現在有 200 塊錢,你現在有 100 塊錢,為了突出重點我也不按正常的結構來畫這個表。

然后我要轉 100 塊給你。

此時事務管理器發起了準備請求,然后我賬上的錢就少了,你賬上的錢就多了,而且**事務管理器還記錄下這次操作的日志**。
此時的數據還是私有版本,別的事務是讀不到的,簡單的理解 Lock 上有值就還是私有的。
可以看到我的記錄 Lock 標記的是 PK,你的記錄標記的是指向我的記錄指針,這個 PK 是隨機選擇的。
然后事務管理器會向被選擇作為 PK 的那條記錄發起提交指令。

此時就會把我的記錄的鎖給抹去了,這等于我的記錄不再是私有版本了,別的事務就都能訪問了。
那你的記錄上還有鎖啊?不用更新嗎?
嘿嘿不需要及時更新,因為訪問你的這條記錄的時候會去根據指針找我的那個記錄,發現記錄已經提交了所以你的記錄就可以被訪問了。
有人說這效率不就差了,每次都要去找一次,別急。
后臺會有個線程來掃描,然后更新把鎖記錄給去了。
這不就穩了嘛。
### 相比于 2PC 的改進
首先 Percolator 在提交階段不需要和所有的參與者交互,主需要和一個參與者打交道,所以這個提交是原子的!**解決了數據不一致問題**。
然后事務管理器會記錄操作日志,這樣當事務管理器掛了之后選舉的新事務管理器就可以通過日志來得知當前的情況從而繼續工作,**解決了單點故障問題**。
并且 Percolator 還會有后臺線程,會掃描事務狀況,在事務管理器宕機之后會回滾各個參與者上的事務。
可以看到相對于 2PC 還是做了很多改進的,也是巧妙的。
其實分布式數據庫還有別的事務模型,不過我也不太熟悉,就不多嗶嗶了,有興趣的同學可以自行了解。
還是挺能拓寬思想的。
## XA 規范
讓我們再回來 2PC,既然說到 2PC 了那么也簡單的提一下 XA 規范,XA 規范是基于兩階段提交的,它實現了兩階段提交協議。
在說 XA 規范之前又得先提一下 DTP 模型,即 Distributed Transaction Processing,這模型規范了分布式事務的模型設計。
而 XA 規范又約束了 DTP 模型中的事務管理器(TM) 和資源管理器(RM)之間的交互,簡單的說就是你們兩之間要按照一定的格式規范來交流!
我們先來看下 XA 約束下的 DTP 模型。

* AP 應用程序,就是我們的應用,事務的發起者。
* RM 資源管理器,簡單的認為就是數據庫,具備事務提交和回滾能力,對應我們上面的 2PC 就是參與者。
* TM 事務管理器,就是協調者了,和每個 RM 通信。
簡單的說就是 AP 通過 TM 來定義事務操作,TM 和 RM 之間會通過 XA 規范進行通信,執行兩階段提交,而 AP 的資源是從 RM 拿的。
從模型上看有三個角色,而實際實現可以由一個角色實現兩個功能,比如 AP 來實現 TM 的功能,TM 沒必要抽出來單獨部署。
### MySQL XA
知曉了 DTP 之后,我們就來看看 XA 在 MySQL 中是如何操作的,不過只有 InnoDB 支持。
簡單的說就是要先定義一個全局唯一的 XID,然后告知每個事務分支要進行的操作。
可以看到圖中執行了兩個操作,分別是改名字和插入日志,等于先注冊下要做的事情,通過 XA START XID 和 XA END XID 來包裹要執行的 SQL。

然后需要發送準備命令,來執行第一階段,也就是除了事務的提交啥都干了的階段。

然后根據準備的情況來選擇執行提交事務命令還是回滾事務命令。

基本上就是這么個流程,不過 MySQL XA 的性能不高這點是需要注意的。
可以看到雖說 2PC 有缺點,但是還是有基于 2PC 的落地實現的,而 3PC 的引出是為了解決 2PC 的一些缺點,但是它整體下來開銷更大,也解決不了網絡分區的問題,我也沒有找到 3PC 的落地實現。
不過我還是稍微提一下,知曉一下就行,純理論。
## 3PC
3PC 的引入是為了解決 2PC 同步阻塞和減少數據不一致的情況。
3PC 也就是多了一個階段,一個詢問的階段,分別是準備、預提交和提交這三個階段。
準備階段單純就是協調者去訪問參與者,類似于你還好嗎?能接請求不。
預提交其實就是 2PC 的準備階段,除了事務的提交啥都干了。
提交階段和 2PC 的提交一致。

3PC 多了一個階段其實就是在執行事務之前來確認參與者是否正常,防止個別參與者不正常的情況下,其他參與者都執行了事務,鎖定資源。
出發點是好的,但是**絕大部分情況下肯定是正常的,所以每次都多了一個交互階段就很不劃算。**
然后 3PC 在參與者處也引入了超時機制,這樣在協調者掛了的情況下,如果已經到了提交階段了,參與者等半天沒收到協調者的情況的話就會自動提交事務。
不過萬一協調者發的是回滾命令呢?你看這就出錯了,數據不一致了。
還有維基百科上說 2PC 參與者準備階段之后,如果協調者掛了,參與者是無法得知整體的情況的,因為大局是協調者掌控的,所以參與者相互之間的狀況它們不清楚。
而 3PC 經過了第一階段的確認,即使協調者掛了參與者也知道自己所處預提交階段是因為已經得到準備階段所有參與者的認可了。
簡單的說就像加了個圍欄,使得各參與者的狀態得以統一。
## 小結 2PC 和 3PC
從上面已經知曉了 2PC 是一個強一致性的同步阻塞協議,性能已經是比較差的了。
而 3PC 的出發點是為了解決 2PC 的缺點,但是多了一個階段就多了一次通訊的開銷,而且是絕大部分情況下無用的通訊。
雖說引入參與者超時來解決協調者掛了的阻塞問題,但是數據還是會不一致。
可以看到 3PC 的引入并沒什么實際突破,而且性能更差了,所以實際只有 2PC 的落地實現。
再提一下,2PC 還是 3PC 都是協議,可以認為是一種指導思想,和真正的落地還是有差別的。

## TCC
不知道大家注意到沒,**不管是 2PC 還是 3PC 都是依賴于數據庫的事務提交和回滾。**
而有時候一些業務它不僅僅涉及到數據庫,可能是發送一條短信,也可能是上傳一張圖片。
所以說事務的提交和回滾就得提升到業務層面而不是數據庫層面了,而**TCC 就是一種業務層面或者是應用層的兩階段提交**。
TCC 分為指代 Try、Confirm、Cancel ,也就是業務層面需要寫對應的三個方法,主要用于跨數據庫、跨服務的業務操作的數據一致性問題。
TCC 分為兩個階段,第一階段是資源檢查預留階段即 Try,第二階段是提交或回滾,如果是提交的話就是執行真正的業務操作,如果是回滾則是執行預留資源的取消,恢復初始狀態。
比如有一個扣款服務,我需要寫 Try 方法,用來凍結扣款資金,還需要一個 Confirm 方法來執行真正的扣款,最后還需要提供 Cancel 來進行凍結操作的回滾,對應的一個事務的所有服務都需要提供這三個方法。
可以看到本來就一個方法,現在需要膨脹成三個方法,所以說 TCC 對業務有很大的侵入,像如果沒有凍結的那個字段,還需要改表結構。
我們來看下流程。

雖說對業務有侵入,但是 TCC 沒有資源的阻塞,每一個方法都是直接提交事務的,如果出錯是通過業務層面的 Cancel 來進行補償,所以也稱補償性事務方法。
這里有人說那要是所有人 Try 都成功了,都執行 Comfirm 了,但是個別 Confirm 失敗了怎么辦?
這時候只能是不停地重試調失敗了的 Confirm 直到成功為止,如果真的不行只能記錄下來,到時候人工介入了。
### TCC 的注意點
這幾個點很關鍵,在實現的時候一定得注意了。

**冪等問題**,因為網絡調用無法保證請求一定能到達,所以都會有重調機制,因此對于 Try、Confirm、Cancel 三個方法都需要冪等實現,避免重復執行產生錯誤。
**空回滾問題**,指的是 Try 方法由于網絡問題沒收到超時了,此時事務管理器就會發出 Cancel 命令,那么需要支持 Cancel 在未執行 Try 的情況下能正常的 Cancel。
**懸掛問題**,這個問題也是指 Try 方法由于網絡阻塞超時觸發了事務管理器發出了 Cancel 命令,**但是執行了 Cancel 命令之后 Try 請求到了,你說氣不氣**。
這都 Cancel 了你來個 Try,對于事務管理器來說這時候事務已經是結束了的,這凍結操作就被“懸掛”了,所以空回滾之后還得記錄一下,防止 Try 的再調用。
## TCC 變體
上面我們說的是通用型的 TCC,它需要改造以前的實現,但是有一種情況是無法改造的,就是**你調用的是別的公司的接口**。
### 沒有 Try 的 TCC
比如坐飛機需要換乘,換乘的又是不同的航空公司,比如從 A 飛到 B,再從 B 飛到 C,只有 A - B 和 B - C 都買到票了才有意義。
這時候的選擇就沒得 Try 了,直接調用航空公司的買票操作,當兩個航空公司都買成功了那就直接成功了,如果某個公司買失敗了,那就需要調用取消訂票接口。
也就是在第一階段直接就執行完整個業務操作了,所以要重點關注回滾操作,如果回滾失敗得有提醒,要人工介入等。
這其實就是 TCC 的思想。

### 異步 TCC
這 TCC 還能異步?其實也是一種折中,比如某些服務很難改造,并且它又不會影響主業務決策,也就是它不那么重要,不需要及時的執行。
這時候可以引入可靠消息服務,通過消息服務來替代個別服務來進行 Try、Confirm、Cancel 。
Try 的時候只是寫入消息,消息還不能被消費,Confirm 就是真正發消息的操作,Cancel 就是取消消息的發送。
這可靠消息服務其實就類似于等下要提到的事務消息,這個方案等于糅合了事務消息和 TCC。
## TCC 小結
可以看到**TCC 是通過業務代碼來實現事務的提交和回滾,對業務的侵入較大,它是業務層面的兩階段提交,**。
它的性能比 2PC 要高,因為不會有資源的阻塞,并且適用范圍也大于 2PC,在實現上要注意上面提到的幾個注意點。
它是業界比較常用的分布式事務實現方式,而且從變體也可以得知,還是得**看業務變通的,不是說你要用 TCC 一定就得死板的讓所有的服務都改造成那三個方法。**
## 本地消息表
本地消息就是利用了本地事務,會在數據庫中存放一直本地事務消息表,在進行本地事務操作中加入了本地消息的插入,即**將業務的執行和將消息放入消息表中的操作放在同一個事務中提交**
這樣本地事務執行成功的話,消息肯定也插入成功,然后再調用其他服務,如果調用成功就修改這條本地消息的狀態。
如果失敗也不要緊,會有一個后臺線程掃描,發現這些狀態的消息,會一直調用相應的服務,一般會設置重試的次數,如果一直不行則特殊記錄,待人工介入處理。
可以看到還是很簡單的,也是一種最大努力通知思想。

## 事務消息
這個其實之前聊過,專門講事務消息,從源碼層面剖析了 RocketMQ 、Kafka 的事務消息實現,以及兩者之間的區別。
## Seata 的實現
首先什么是 Seata ,摘抄官網的一段話。
> Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。
可以看到提供了很多模式,我們先來看看 AT 模式。
### AT模式
AT 模式就是兩階段提交,前面我們提到了兩階段提交有同步阻塞的問題,效率太低了,那 Seata 是怎么解決的呢?
AT 的一階段直接就把事務提交了,直接釋放了本地鎖,這么草率直接提交的嘛?當然不是,這里和本地消息表有點類似,就是利用本地事務,執行真正的事務操作中還會插入回滾日志,然后在一個事務中提交。
**這回滾日志怎么來的**?
通過框架代理 JDBC 的一些類,在執行 SQL 的時候解析 SQL 得到執行前的數據鏡像,然后執行 SQL ,再得到執行后的數據鏡像,然后把這些數據組裝成回滾日志。
再伴隨的這個本地事務的提交把回滾日志也插入到數據庫的 UNDO\_LOG 表中(所以數據庫需要有一張UNDO\_LOG 表)。
這波操作下來在一階段就可以沒有后顧之憂的提交事務了。
然后一階段如果成功,那么二階段可以異步的刪除那些回滾日志,如果一階段失敗那么可以通過回滾日志來反向補償恢復。
這時候有細心的同學想到了,萬一中間有人改了這條數據怎么辦?你這鏡像就不對了啊?
所以說還有個全局鎖的概念,**在事務提交前需要拿到全局鎖**(可以理解為對這條數據的鎖),然后才能順利提交本地事務。
如果一直拿不到那就需要回滾本地事務了。
**官網的示例很好,我就不自己編了,以下部分內容摘抄自 Seata 官網的示例**:
此時有兩個事務,分別是 tx1、和 tx2,分別對 a 表的 m 字段進行更新操作,m 的初始值 1000。
tx1 先開始,開啟本地事務,拿到本地鎖,更新操作 m = 1000 - 100 = 900。本地事務提交前,先拿到該記錄的 全局鎖 ,本地提交釋放本地鎖。
tx2 后開始,開啟本地事務,拿到本地鎖,更新操作 m = 900 - 100 = 800。本地事務提交前,嘗試拿該記錄的 全局鎖 ,tx1 全局提交前,該記錄的全局鎖被 tx1 持有,tx2 需要重試等待全局鎖 。

可以看到 tx2 的修改被阻塞了,之后重試拿到全局鎖之后就能提交然后釋放本地鎖。
如果 tx1 的二階段全局回滾,則 tx1 需要重新獲取該數據的本地鎖,進行反向補償的更新操作,實現分支的回滾。
此時,如果 tx2 仍在等待該數據的全局鎖,同時持有本地鎖,則 tx1 的分支回滾會失敗。分支的回滾會一直重試,直到 tx2 的全局鎖等鎖超時,放棄全局鎖并回滾本地事務釋放本地鎖,tx1 的分支回滾最終成功。
**因為整個過程全局鎖在 tx1 結束前一直是被 tx1 持有的,所以不會發生臟寫的問題**。

然后**AT 模式默認全局是讀未提交的隔離級別**,如果應用在特定場景下,必需要求全局的讀已提交 ,可以通過 SELECT FOR UPDATE 語句的代理。
當然前提是你本地事務隔離級別是讀已提交及以上。
### AT 模式小結
可以看到通過代理來無侵入的得到數據的前后鏡像,組裝成回滾日志伴隨本地事務一起提交,解決了兩階段的同步阻塞問題。
并且利用全局鎖來實現寫隔離。
為了總體性能的考慮,默認是讀未提交隔離級別,只代理了 SELECT FOR UPDATE 來進行讀已提交的隔離。
**這其實就是兩階段提交的變體實現**。
## TCC 模式
沒什么花頭,就是咱們上面分析的需要搞三個方法,**然后把自定義的分支事務納入到全局事務的管理中**
我貼一張官網的圖應該挺清晰了。

## Saga 模式
這個 Saga 是 Seata 提供的長事務解決方案,適用于業務流程多且長的情況下,這種情況如果要實現一般的 TCC 啥的可能得嵌套多個事務了。
并且有些系統無法提供 TCC 這三種接口,比如老項目或者別人公司的,所以就搞了個 Saga 模式,這個 Saga 是在 1987 年 Hector & Kenneth 發表的論?中提出的。
那 Saga 如何做呢?來看下這個圖。

假設有 N 個操作,直接從 T1 開始就是直接執行提交事務,然后再執行 T2,可以看到就是無鎖的直接提交,到 T3 發現執行失敗了,然后就進入 Compenstaing 階段,開始一個一個倒回補償了。
思想就是一開始蒙著頭干,別慫,出了問題咱們再一個一個改回去唄。
可以看到這種情況是不保證事務的隔離性的,并且 Saga 也有 TCC 的一樣的注意點,需要空補償,防懸掛和冪等。
而且極端情況下會因為數據被改變了導致無法回滾的情況。比如第一步給我打了 2 萬塊錢,我給取出來花了,這時候你回滾,我賬上余額已經 0 了,你說怎么辦嘛?難道給我還搞負的不成?
這種情況只能在業務流程上入手,我寫代碼其實一直是這樣寫的,就拿買皮膚的場景來說,**我都是先扣錢再給皮膚。**
假設先給皮膚扣錢失敗了不就白給了嘛?這錢你來補啊?你覺得用戶會來反饋說皮膚給了錢沒扣嘛?
可能有小機靈鬼說我到時候把皮膚給改回去,嘿嘿這種事情確實發生過,嘖嘖,被罵的真慘。
所以**正確的流程應該是先扣錢再給皮膚**,錢到自己袋里先,皮膚沒給成功用戶自然而然會找過來,這時候再給他唄,雖說可能你寫出了個 BUG ,但是還好不是個白給的 BUG。
所以說這點在編碼的時候還是得注意下的。

## 最后
可以看到分布式事務還是會有各種問題,一般分布式事務的實現還是只能達到最終一致性。
極端情況下還是得人工介入,所以做好日志記錄很關鍵。
還有編碼的業務流程,要往利于公司的方向寫,就例如先拿到用戶的錢,再給用戶東西這個方向,切記。
在上分布式事務之前想想,有沒有必要,能不能改造一下避免分布式事務?
再極端一點,你的業務有沒有必要上事務?
- 一.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協議模塊