前言
大家都知道 Redis 是一個內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)都存儲在內(nèi)存中,這也是 Redis 非常快的原因之一。雖然速度提上來了,但是如果數(shù)據(jù)一直放在內(nèi)存中,是非常容易丟失的。比如 服務(wù)器關(guān)閉或宕機了,內(nèi)存中的數(shù)據(jù)就木有了。為了解決這一問題,Redis 提供了 持久化 機制。分別是RDB以及AOF持久化。
RDB
什么是 RDB 持久化?
RDB 持久化可以在指定的時間間隔內(nèi)生成數(shù)據(jù)集的時間點快照(point-in-time snapshot)。

RDB 的優(yōu)點?
RDB 是一種表示某個即時點的 Redis 數(shù)據(jù)的緊湊文件。RDB 文件適用于備份。例如,你可能想要每小時歸檔最近24小時的 RDB 文件,每天保存近30天的 RDB 快照。這允許你很容易的恢復不同版本的數(shù)據(jù)集以容災。
RDB 非常適合于災難恢復,作為一個緊湊的單一文件,可以被傳輸?shù)竭h程的數(shù)據(jù)中心。
RDB 最大化了 Redis 的性能。因為 Redis 父進程持久化時唯一需要做的是啟動(fork)一個子進程,由子進程完成所有剩余的工作。父進程實例不需要執(zhí)行像磁盤IO這樣的操作。
RDB 在重啟保存了大數(shù)據(jù)集的實例比 AOF 快。
RDB 的缺點?
當你需要在Redis停止工作(例如停電)時最小化數(shù)據(jù)丟失,RDB可能不太好。你可以配置不同的保存點(save point)來保存RDB文件(例如,至少5分鐘和對數(shù)據(jù)集100次寫之后,但是你可以有多個保存點)。然而,你通常每隔5分鐘或更久創(chuàng)建一個RDB快照,所以一旦Redis因為任何原因沒有正確關(guān)閉而停止工作,你就得做好最近幾分鐘數(shù)據(jù)丟失的準備了。
RDB需要經(jīng)常調(diào)用fork()子進程來持久化到磁盤。如果數(shù)據(jù)集很大的話,fork()比較耗時,結(jié)果就是,當數(shù)據(jù)集非常大并且CPU性能不夠強大的話,Redis會停止服務(wù)客戶端幾毫秒甚至一秒。AOF也需要fork(),但是你可以調(diào)整多久頻率重寫日志而不會有損(trade-off)持久性(durability)。
RDB 文件的創(chuàng)建與載入
有個兩個 Redis 命令可以用于生成 RDB 文件,一個是SAVE,另一個是BGSAVE。
SAVE命令會阻塞 Redis 服務(wù)器進程,直到 RDB 文件創(chuàng)建完畢為止,在服務(wù)器進程阻塞期間,服務(wù)器不能處理任何命令請求。
> SAVE? ? // 一直等到 RDB 文件創(chuàng)建完畢
OK
和 SAVE 命令直接阻塞服務(wù)器進程不同的是,BGSAVE 命令會派生出一個子進程,然后由子進程負責創(chuàng)建 RDB 文件,服務(wù)器進程(父進程)繼續(xù)處理命令進程。
執(zhí)行fork的時候操作系統(tǒng)(類Unix操作系統(tǒng))會使用寫時復制(copy-on-write)策略,即fork函數(shù)發(fā)生的一刻父子進程共享同一內(nèi)存數(shù)據(jù),當父進程要更改其中某片數(shù)據(jù)時(如執(zhí)行一個寫命令 ),操作系統(tǒng)會將該片數(shù)據(jù)復制一份以保證子進程的數(shù)據(jù)不受影響,所以新的RDB文件存儲的是執(zhí)行fork一刻的內(nèi)存數(shù)據(jù)。
> BGSAVE? // 派生子進程,并由子進程創(chuàng)建 RDB 文件
Background saving started
生成 RDB 文件由兩種方式:一種是手動,就是上邊介紹的用命令的方式;另一種是自動的方式。
接下來詳細介紹一下自動生成 RDB 文件的流程。
Redis 允許用戶通過設(shè)置服務(wù)器配置的save選項,讓服務(wù)器每隔一段時間自動執(zhí)行一次 BGSAVE 命令。
用戶可以通過在 redis.conf 配置文件中的 SNAPSHOTTING 下 save 選項設(shè)置多個保存條件,但只要其中任意一個條件被滿足,服務(wù)器就會執(zhí)行 BGSAEVE 命令。
如,以下配置:
save 900 1
save 300 10
save 60 10000
上邊三個配置的含義是:
服務(wù)器在 900 秒內(nèi),對數(shù)據(jù)庫進行了至少 1 次修改。
服務(wù)器在 300 秒內(nèi),對數(shù)據(jù)庫進行了至少 10 次修改。
服務(wù)器在 60 秒內(nèi),對數(shù)據(jù)庫進行了至少 10000 次修改。
如果沒有手動去配置 save 選項,那么服務(wù)器會為 save 選項配置默認參數(shù):
save 900 1
save 300 10
save 60 10000
接著,服務(wù)器就會根據(jù) save 選項的配置,去設(shè)置服務(wù)器狀態(tài) redisServer 結(jié)構(gòu)的 saveparams 屬性:
struct redisServer{
? // ...
? // 記錄了保存條件的數(shù)組
? struct saveparams *saveparams;
? // ...
};
saveparams 屬性是一個數(shù)組,數(shù)組中的每一個元素都是一個 saveparam 結(jié)構(gòu),每個 saveparam 結(jié)構(gòu)都保存了一個 save 選項設(shè)置的保存條件:
struct saveparam {
? // 秒數(shù)
? time_t seconds;
? // 修改數(shù)
? int changes;
};
除了 saveparams 數(shù)組之外,服務(wù)器狀態(tài)還維持著一個 dirty 計數(shù)器,以及一個 lastsave 屬性;
struct redisServer {
? ? // ...
? ? // 修改計數(shù)器
? ? long long dirty;
? ? // 上一次執(zhí)行保存時間
? ? time_t lastsave;
? ? // ...
}
dirty 計數(shù)器記錄距離上一次成功執(zhí)行 SAVE 或 BGSAVE 命令之后,服務(wù)器對數(shù)據(jù)庫狀態(tài)(服務(wù)器中的所有數(shù)據(jù)庫)進行了多少次修改(包括寫入、刪除、更新等操作)。
lastsave 屬性是一個 UNIX 時間戳,記錄了服務(wù)器上一次執(zhí)行 SAVE 或 BGSAVE 命令的時間。
檢查條件是否滿足觸發(fā) RDB
Redis 的服務(wù)器周期性操作函數(shù) serverCron 默認每隔 100 毫秒執(zhí)行一次,該函數(shù)用于對正在運行的服務(wù)器進行維護,它的其中一項工作就是檢查 save 選項所設(shè)置的保存條件是否已經(jīng)滿足,如果滿足的話就執(zhí)行 BGSAVE 命令。
Redis serverCron 源碼解析如下:
程序會遍歷并檢查 saveparams 數(shù)組中的所有保存條件,只要有任意一個條件被滿足,服務(wù)器就會執(zhí)行 BGSAVE 命令。
下面是 rdbSaveBackground 的源碼流程:
RDB 文件結(jié)構(gòu)
下圖展示了一個完整 RDB 文件所包含的各個部分。

redis 文件的最開頭是REDIS部分,這個部分的長度是 5 字節(jié),保存著 “REDIS” 五個字符。通過這五個字符,程序可以在載入文件時,快速檢查所載入的文件是否時 RDB 文件。
db_version長度為 4 字節(jié),他的值時一個字符串表示的整數(shù),這個整數(shù)記錄了 RDB 文件的版本號,比如 “0006” 就代表 RDB 文件的版本為第六版。
database部分包含著零個或任意多個數(shù)據(jù)庫,以及各個數(shù)據(jù)庫中的鍵值對數(shù)據(jù):
如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為空(所有數(shù)據(jù)庫都是空的),那么這個部分也為空,長度為 0 字節(jié)。
如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為非空(有至少一個數(shù)據(jù)庫非空),那么這個部分也為非空,根據(jù)數(shù)據(jù)庫所保存鍵值對的數(shù)量、類型和內(nèi)容不同,這個部分的長度也會有所不同。
EOF常量的長度為 1 字節(jié),這個常量標志著 RDB 文件正文內(nèi)容的結(jié)束,當讀入程序遇到這個值后,他知道所有數(shù)據(jù)庫的所有鍵值對已經(jīng)載入完畢了。
check_sum是一個 8 字節(jié)長的無符號整數(shù),保存著一個校驗和,這個校驗和時程序通過對 REDIS、db_version、database、EOF 四個部分的內(nèi)容進行計算得出的。服務(wù)器在載入 RDB 文件時,會將載入數(shù)據(jù)所計算出的校驗和與 check_sum 所記錄的校驗和進行對比,以此來檢查 RDB 是否有出錯或者損壞的情況。
舉個例子:下圖是一個 0 號數(shù)據(jù)庫和 3 號數(shù)據(jù)庫的 RDB 文件。第一個就是 “REDIS” 表示是一個 RDB 文件,之后的 “0006” 表示這是第六版的 REDIS 文件,然后是兩個數(shù)據(jù)庫,之后就是 EOF 結(jié)束標識符,最后就是 check_sum。

AOF 持久化
什么是 AOF 持久化
AOF持久化方式記錄每次對服務(wù)器寫的操作,當服務(wù)器重啟的時候會重新執(zhí)行這些命令來恢復原始的數(shù)據(jù),AOF命令以redis協(xié)議追加保存每次寫的操作到文件末尾.Redis還能對AOF文件進行后臺重寫,使得AOF文件的體積不至于過大.

AOF 的優(yōu)點?
使用AOF 會讓你的Redis更加耐久: 你可以使用不同的fsync策略:無fsync,每秒fsync,每次寫的時候fsync.使用默認的每秒fsync策略,Redis的性能依然很好(fsync是由后臺線程進行處理的,主線程會盡力處理客戶端請求),一旦出現(xiàn)故障,你最多丟失1秒的數(shù)據(jù).
AOF文件是一個只進行追加的日志文件,所以不需要寫入seek,即使由于某些原因(磁盤空間已滿,寫的過程中宕機等等)未執(zhí)行完整的寫入命令,你也也可使用redis-check-aof工具修復這些問題.
Redis 可以在 AOF 文件體積變得過大時,自動地在后臺對 AOF 進行重寫: 重寫后的新 AOF 文件包含了恢復當前數(shù)據(jù)集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創(chuàng)建新 AOF 文件的過程中,會繼續(xù)將命令追加到現(xiàn)有的 AOF 文件里面,即使重寫過程中發(fā)生停機,現(xiàn)有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創(chuàng)建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,并開始對新 AOF 文件進行追加操作。
AOF 文件有序地保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作, 這些寫入操作以 Redis 協(xié)議的格式保存, 因此 AOF 文件的內(nèi)容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執(zhí)行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那么只要停止服務(wù)器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重啟 Redis , 就可以將數(shù)據(jù)集恢復到 FLUSHALL 執(zhí)行之前的狀態(tài)。
AOF 的缺點?
對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常要大于 RDB 文件的體積。
根據(jù)所使用的 fsync 策略,AOF 的速度可能會慢于 RDB 。 在一般情況下, 每秒 fsync 的性能依然非常高, 而關(guān)閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。
AOF持久化的實現(xiàn)
AOF持久化功能的實現(xiàn)可以分為命令追加(append)、文件寫入、文件同步(sync)三個步驟。
命令追加
當 AOF 持久化功能處于打開狀態(tài)時,服務(wù)器在執(zhí)行完一個寫命令之后,會以協(xié)議格式將被執(zhí)行的寫命令追加到服務(wù)器狀態(tài)的 aof_buf 緩沖區(qū)的末尾。
struct redisServer {
? // ...
? // AOF 緩沖區(qū)?
? sds aof_buf;
? // ..
};
如果客戶端向服務(wù)器發(fā)送以下命令:
> set KEY VALUE
OK
那么服務(wù)器在執(zhí)行這個 set 命令之后,會將以下協(xié)議內(nèi)容追加到 aof_buf 緩沖區(qū)的末尾;
*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
AOF 文件的寫入與同步
Redis的服務(wù)器進程就是一個事件循環(huán)(loop),這個循環(huán)中的文件事件負責接收客戶端 的命令請求,以及向客戶端發(fā)送命令回復,而時間事件則負責執(zhí)行像 serverCron 函數(shù)這樣需 要定時運行的函數(shù)。
因為服務(wù)器在處理文件事件時可能會執(zhí)行寫命令,使得一些內(nèi)容被追加到aof_buf緩沖區(qū) 里面,所以在服務(wù)器每次結(jié)束一個事件循環(huán)之前,它都會調(diào)用flushAppendOnlyFile函數(shù),考 慮是否需要將aof_buf緩沖區(qū)中的內(nèi)容寫入和保存到AOF文件里面,這個過程可以用以下偽代 碼表示:
def eventLoop():
? while True:
? #處理文件事件,接收命令請求以及發(fā)送命令回復
? #處理命令請求時可能會有新內(nèi)容被追加到 aof_buf緩沖區(qū)中
? processFileEvents()
? #處理時間事件
? processTimeEvents()
? #考慮是否要將 aof_buf中的內(nèi)容寫入和保存到 AOF文件里面
? flushAppendOnlyFile()
flushAppendOnlyFile函數(shù)的行為由服務(wù)器配置的appendfsync選項的值來決定,各個不同 值產(chǎn)生的行為如下表所示。
appendfsync 選項的值flushAppendOnlyFile 函數(shù)的行為
always將 aof_buf 緩沖區(qū)中的所有內(nèi)容寫入并同步到 AOF 文件
everysec將 aof_buf 緩沖區(qū)中的所有內(nèi)容寫入到 AOF 文件,如果上次同步 AOF 文件的時間距離現(xiàn)在超過一秒鐘,那么再次對 AOF 文件進行同步,并且這個同步操作是由一個線程專門負責執(zhí)行的
no將 aof_buf 緩沖區(qū)中的所有內(nèi)容寫入到 AOF 文件,但并不對 AOF 文件進行同步,何時同步由操作系統(tǒng)來決定
如果用戶沒有主動為appendfsync選項設(shè)置值,那么appendfsync選項的默認值為everysec。
寫到這里有的小伙伴可能會對上面說的寫入和同步含義弄混,這里說一下:
寫入:將 aof_buf 中的數(shù)據(jù)寫入到 AOF 文件中。
同步:調(diào)用 fsync 以及 fdatasync 函數(shù),將 AOF 文件中的數(shù)據(jù)保存到磁盤中。
通俗地講就是,你要往一個文件寫東西,寫的過程就是寫入,而同步則是將文件保存,數(shù)據(jù)落到磁盤上。
大家之前看文章的時候是不是大多都說 AOF 最多丟失一秒鐘的數(shù)據(jù),那是因為 redis AOF 默認是 everysec 策略,這個策略每秒執(zhí)行一次,所以 AOF 持久化最多丟失一秒鐘的數(shù)據(jù)。
AOF 文件的載入與數(shù)據(jù)還原
因為AOF文件里面包含了重建數(shù)據(jù)庫狀態(tài)所需的所有寫命令,所以服務(wù)器只要讀入并重新執(zhí)行一遍AOF文件里面保存的寫命令,就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫狀態(tài)。 Redis讀取AOF文件并還原數(shù)據(jù)庫狀態(tài)的詳細步驟如下:
創(chuàng)建一個不帶網(wǎng)絡(luò)連接的偽客戶端(fake client):因為Redis的命令只能在客戶端上 下文中執(zhí)行,而載入AOF文件時所使用的命令直接來源于AOF文件而不是網(wǎng)絡(luò)連接,所以服 務(wù)器使用了一個沒有網(wǎng)絡(luò)連接的偽客戶端來執(zhí)行AOF文件保存的寫命令,偽客戶端執(zhí)行命令 的效果和帶網(wǎng)絡(luò)連接的客戶端執(zhí)行命令的效果完全一樣。
從AOF文件中分析并讀取出一條寫命令。
使用偽客戶端執(zhí)行被讀出的寫命令。
一直執(zhí)行步驟2和步驟3,直到AOF文件中的所有寫命令都被處理完畢為止。
當完成以上步驟之后,AOF文件所保存的數(shù)據(jù)庫狀態(tài)就會被完整地還原出來,整個過程 如下圖所示。

AOF 重寫
因為AOF持久化是通過保存被執(zhí)行的寫命令來記錄數(shù)據(jù)庫狀態(tài)的,所以隨著服務(wù)器運行 時間的流逝,AOF文件中的內(nèi)容會越來越多,文件的體積也會越來越大,如果不加以控制的 話,體積過大的AOF文件很可能對Redis服務(wù)器、甚至整個宿主計算機造成影響,并且AOF文 件的體積越大,使用AOF文件來進行數(shù)據(jù)還原所需的時間就越多。
如 客戶端執(zhí)行了以下命令是:
> rpush list "A" "B"
OK
> rpush list "C"
OK
> rpush list "D"
OK
> rpush list "E" "F"
OK
那么光是為了記錄這個list鍵的狀態(tài),AOF文件就需要保存四條命令。
對于實際的應用程度來說,寫命令執(zhí)行的次數(shù)和頻率會比上面的簡單示例要高得多,所 以造成的問題也會嚴重得多。 為了解決AOF文件體積膨脹的問題,Redis提供了AOF文件重寫(rewrite)功能。通過該 功能,Redis服務(wù)器可以創(chuàng)建一個新的AOF文件來替代現(xiàn)有的AOF文件,新舊兩個AOF文件所 保存的數(shù)據(jù)庫狀態(tài)相同,但新AOF文件不會包含任何浪費空間的冗余命令,所以新AOF文件 的體積通常會比舊AOF文件的體積要小得多。 在接下來的內(nèi)容中,我們將介紹AOF文件重寫的實現(xiàn)原理,以及BGREWEITEAOF命令 的實現(xiàn)原理。
雖然Redis將生成新AOF文件替換舊AOF文件的功能命名為“AOF文件重寫”,但實際上, AOF文件重寫并不需要對現(xiàn)有的AOF文件進行任何讀取、分析或者寫入操作,這個功能是通 過讀取服務(wù)器當前的數(shù)據(jù)庫狀態(tài)來實現(xiàn)的。
就像上面的情況,服務(wù)器完全可以將這六條命令合并成一條。
> rpush list "A" "B" "C" "D" "E" "F"
??除了上面列舉的列表鍵之外,其他所有類型的鍵都可以用同樣的方法去減少 AOF文件中的命令數(shù)量。首先從數(shù)據(jù)庫中讀取鍵現(xiàn)在的值,然后用一條命令去記錄鍵值對,代替之前記錄這個鍵值對的多條命令,這就是AOF重寫功能的實現(xiàn)原理。
??在實際中,為了避免在執(zhí)行命令時造成客戶端輸入緩沖區(qū)溢出,重寫程序在處理列表、 哈希表、集合、有序集合這四種可能會帶有多個元素的鍵時,會先檢查鍵所包含的元素數(shù) 量,如果元素的數(shù)量超過了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那 么重寫程序?qū)⑹褂枚鄺l命令來記錄鍵的值,而不單單使用一條命令。 在目前版本中,REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值為64,這也就是 說,如果一個集合鍵包含了超過64個元素,那么重寫程序會用多條SADD命令來記錄這個集 合,并且每條命令設(shè)置的元素數(shù)量也為64個。
AOF 后臺重寫
??AOF 重寫會執(zhí)行大量的寫操作,這樣會影響主線程,所以redis AOF 重寫放到了子進程去執(zhí)行。這樣可以達到兩個目的:
子進程進行AOF重寫期間,服務(wù)器進程(父進程)可以繼續(xù)處理命令請求。
子進程帶有服務(wù)器進程的數(shù)據(jù)副本,使用子進程而不是線程,可以在避免使用鎖的情況 下,保證數(shù)據(jù)的安全性。
但是有一個問題,當子進程重寫數(shù)據(jù)時,主進程依然在處理新的數(shù)據(jù),這也就會造成數(shù)據(jù)不一致情況。
為了解決這種數(shù)據(jù)不一致問題,Redis服務(wù)器設(shè)置了一個AOF重寫緩沖區(qū),這個緩沖區(qū)在 服務(wù)器創(chuàng)建子進程之后開始使用,當Redis服務(wù)器執(zhí)行完一個寫命令之后,它會同時將這個寫 命令發(fā)送給AOF緩沖區(qū)和AOF重寫緩沖區(qū),如下圖:

這也就是說,在子進程執(zhí)行AOF重寫期間,服務(wù)器進程需要執(zhí)行以下三個工作:
執(zhí)行客戶端發(fā)來的命令。
將執(zhí)行后的寫命令追加到AOF緩沖區(qū)。
將執(zhí)行后的寫命令追加到AOF重寫緩沖區(qū)。
這樣一來可以保證:
AOF緩沖區(qū)的內(nèi)容會定期被寫入和同步到AOF文件,對現(xiàn)有AOF文件的處理工作會如常 進行。
從創(chuàng)建子進程開始,服務(wù)器執(zhí)行的所有寫命令都會被記錄到AOF重寫緩沖區(qū)里面。
當子進程完成AOF重寫工作之后,它會向父進程發(fā)送一個信號,父進程在接到該信號之 后,會調(diào)用一個信號處理函數(shù),并執(zhí)行以下工作:
將AOF重寫緩沖區(qū)中的所有內(nèi)容寫入到新AOF文件中,這時新AOF文件所保存的數(shù) 據(jù)庫狀態(tài)將和服務(wù)器當前的數(shù)據(jù)庫狀態(tài)一致。
對新的AOF文件進行改名,原子地(atomic)覆蓋現(xiàn)有的AOF文件,完成新舊兩個 AOF文件的替換。
這個信號處理函數(shù)執(zhí)行完畢之后,父進程就可以繼續(xù)像往常一樣接受命令請求了。
在整個AOF后臺重寫過程中,只有信號處理函數(shù)執(zhí)行時會對服務(wù)器進程(父進程)造成 阻塞,在其他時候,AOF后臺重寫都不會阻塞父進程,這將AOF重寫對服務(wù)器性能造成的影 響降到了最低。
Redis 混合持久化
Redis 還可以同時使用 AOF 持久化和 RDB 持久化。 在這種情況下, 當 Redis 重啟時, 它會優(yōu)先使用 AOF 文件來還原數(shù)據(jù)集, 因為 AOF 文件保存的數(shù)據(jù)集通常比 RDB 文件所保存的數(shù)據(jù)集更完整。但是 AOF 恢復比較慢,Redis 4.0 推出了混合持久化。
混合持久化: 將 rdb 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是 自持久化開始到持久化結(jié)束 的這段時間發(fā)生的增量 AOF 日志,通常這部分 AOF 日志很小。
于是在 Redis 重啟的時候,可以先加載RDB的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。
覺得文章不錯的話,小伙伴們麻煩點個贊、關(guān)個注、轉(zhuǎn)個發(fā)一下唄~你的支持就是我寫文章的動力。
最后小編在學習過程中整理了一些學習資料,可以分享給做java的工程師朋友們,相互交流學習,需要的可以加入我的學習交流群323432957即可免費獲取Java架構(gòu)學習資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構(gòu)資料)