做者:唐劉sql
不久以前,FoundationDB (後面用 fdb 簡化) 從新開源,對於你們來講,這真的是一個很是好的消息。我也在第一時間下載了 fdb 的源碼,開始研究,一方面是看咱們能在什麼方面可以借鑑,另外一方面也是須要給一些朋友回答,TiKV 到底跟 fdb 有什麼不同這樣的問題。緩存
關於 fdb 的研究,本身預計會有幾篇,此次是第一篇,來聊聊我最關注的一個問題 - fdb 是如何實現分佈式事務的。app
在開始介紹以前,要先說說 fdb 的關鍵組件。分佈式
全部的 clients 和 servers 都是經過 cluster file 來鏈接到 fdb cluster,而這個 cluster file 就包含的是 coordinators 的 IP:PORT 列表。全部的 clients 和 servers 都會使用 coordinators 鏈接到 cluster controller。性能
Cluster controller 是經過選舉產生的(fdb 貌似使用的是 Paxos,這個後面會詳細研究一下)。Cluster Controller 就是控制整個集羣的,它是全部進程的入口,會監控進程是否掛掉,告訴某個進程相關的 role,以及在全部進程之間傳遞系統的信息。Clients 也經過 cluster controller 來實時的同步最新的 proxies。學習
Master 主要是用來協調寫子系統的,一個寫子系統包括 master,proxies,resolvers 和 transaction logs。Proxy,resolver 和 transaction log 是一個總體單元,若是任意一個失敗了,那麼咱們就會從新找一個來將他們所有替換。Master 會給 proxies 分配 commit versions,對數據進行分佈,以及全局的流速控制。優化
Proxies 會提供 read versions,提交事務以及跟蹤 storage servers 的 key ranges。若是要提供一個 read version,一個 proxy 會問其餘全部的 proxies 當前最大的 committed version,而且同步的檢查 transaction logs 並無被中止。Master 流速控制可能會減緩提供 read versions 的頻率。server
對於一次事務提交,當只有下面操做所有完成,才能認爲成功:sqlite
從 master 獲得一個 commit version進程
使用 resolvers 來肯定當前事務並無跟以前已經提交的事務衝突
讓 transaction 持久化到 transaction logs
全部以 xff 開頭的 key 是系統保留前綴,用來存放系統的元信息。任何對這段 key range 的修改都會 通-- 過 resolvers 同步到全部的 proxies。元信息包括數據的 key ranges 以及哪些 storage servers 有這些 range,其實也就是數據的路由表了。Proxies 也給 clients 提供相關的信息,讓 clients 進行緩存,若是緩存缺失,就從 proxies 從新更新。
Transaction logs 會按照 version 的順序接受 proxy 發過來的提交,並會使用 append only 的方式將修改的提交持久化到硬盤。在數據被寫入到磁盤的時候,也會通知 storage servers 有相關的修改操做,讓 storage servers 去獲取而且 apply 到 storage servers 裏面。
Resolvers 用來肯定不一樣事務的衝突。當一個事務的 read version,讀取了一個 key,在 commit 以前,另外一個事務寫入了新的值,這時候就會有衝突。 Resovler 會在內存裏面保存 5s 的全部寫入提交,用來判斷衝突,這也就是意味着,fdb 的事務執行時間不能超過 5s。
Storage servers 就是存放數據的地方,fdb 會將數據按照 range 切分,存儲到不一樣的 storage servers 上面。Storage servers 會在內存裏面保存最近 5s 的修改(Versioned data),若是一個 client 的 read version 超過了 5s,那就會過時出錯了。Storage server 有 ssd 和 memory 兩種,ssd 其實用的是 sqlite3。
上面大概介紹了 fdb 的關鍵組件,這裏就先來講說事務。Clients 會先用一個 read version 讀取全部的數據,而後在本地修改,最後再將全部的修改一塊兒提交到 proxies,這其實也就是一個樂觀事務模型。具體流程以下:
開始事務
Clients 從 proxy 獲取一個 read version
Proxy 會批量接受 clients 的請求,若是超過了限流控制,額外的請求會排隊
Proxy 問其它的 proxies 當前最大的 commit version
Proxy 返回最大的 commit version 做爲 read version
讀流程
Client 根據 read version 以及須要訪問的數據的 key 或者 key range 找到對應的 storage servers
Storage server 接受到以後,若是發現 version 太老,結果返回錯誤。若是發現數據還不存在,就等或者超時
Storage server 會根據 read version 找對應的數據,並返回給 client
提交事務
Client 將修改,read version 以及 read ranges 和 write ranges 提交給 proxy
Proxy 仍然是批量的接受請求
Proxy 將 range 切分而且發到不一樣的 resolvers,若是 resolver 判斷有衝突,結束事務
Proxy 經過 master 獲得最近的 commit version
Proxy 將修改的數據按照實際的數據分佈切分,加上 tag,推送到 transaction log servers
Transaction log servers 回覆 proxy 說 log 已經落盤
Proxy 給 client 返回事務提交成功
能夠看到,整個流程仍是很簡單的,這裏還須要注意幾個後臺流程。一個是 storage server 從 transaction logs 讀取數據:
根據提供的 version 和 tag 從 transaction logs 拿數據
將數據讀入到 storage server 的 Versioned data
將數據寫入 storage engine
另外一個就是 version 的更新,proxies 會按期的生成一個空的 commit request 來增長 commit version,這樣 transaction logs 和 storage servers 的 version 都能增長,就能處理一個集羣若是沒有任何寫入,後面新的讀取也能按照 version 讀到對應的版本,不會無限制的等待。若是個人 read version 比當前 storage server 的最大 version 要大,其實並不能保證讀到正確的數據。爲啥會作這個,主要是 fdb 用的時間戳來當的 version。
上面僅僅是對 fdb 事務流程的簡單介紹,幾個 concern 的點:
能夠看到,fdb 在 resolver 那邊其實就是將事務排隊了,因此雖然外面看起來是樂觀事務,但對於衝突嚴重的狀況,性能也比較不錯。以前我一直覺得 resovler 會是個單點,但後面知道 resolver 也是能夠 scale 的。並且 fdb 本身也說作了不少的優化,保證了整個的性能。
後面我會詳盡的搗鼓折騰下 FoundationDB,作下 benchmark,也正在將它集成到咱們的 YCSB 裏面,畢竟對我來講,至少 fdb 那套 deterministic 理念是能夠借鑑學習的。若是你對咱們相關的 TiKV 工做感興趣,歡迎聯繫我 tl@pingcap.com。