在上篇文章中,咱們講解了 Raft Propose 的 Commit 和 Apply 情景分析,相信你們對 TiKV 的 Raft 寫流程有了大概瞭解。這篇文章將嘗試向你們較爲完整的介紹下 TiKV 中的 Raft 讀流程的實現,特別是 read index 和 lease read(或稱 local read)。關於 read index 和 lease read 的介紹和理論基礎,請你們參閱 TiKV 功能介紹 - Lease Read 或者 Raft 論文第 6.4 節,不在這裏贅述。緩存
如何發起 Raft 讀請求?安全
TiKV 的實現是分層的,不一樣模塊負責不一樣事情,下圖直觀地介紹了 TiKV 的模塊的層級關係。 網絡
TiKV 中全部 Raft 相關的邏輯都在 Raftstore 模塊,如何發起 Raft 讀請求就是說如何經過 Raftstore 發起讀請求。Raftstore 對外(TXN/MVCC)提供接口叫作 RaftStoreRouter
,它提供了多方s法,但能供外面發起讀寫請求的只有一個,叫作 send_command
。多線程
全部的讀寫請求統一使用這個方法發起。當操做完成後,無論成功與否,都調用 cb: Callbck<E>
,並將回覆傳入。app
這篇文章接下來的部分將圍繞圖中黃色部分展開。函數
讀請求有哪些?spa
既然這麼問,確定意味着 TiKV 中有多個不一樣類型的讀請求。這就須要瞭解下 RaftCmdRequest
的構成了。TiKV 對外的請求都是 Protocol buffer message,RaftCmdRequest
定義在 kvproto/raft_cmd.proto,它包含了全部 TiKV 支持的讀寫請求。線程
上面代碼中加粗的就是 TiKV 目前支持的幾種讀請求。code
注意:不要把 ReadIndexRequst 和 Read Index 搞混。ReadIndexRequest 是一種讀的請求,ReadIndex 是一種處理讀請求的方式。接口
Raft 如何處理讀請求?
咱們以平常使用中最多見的 SnapRequest 爲例,說一下 Read Index 和 Local read 的流程。
在 TXN/MVCC 層經過 send_command
發起一個讀請求後,Raftstore 中對應的 PeerFsm (就是一個 Raft 狀態機)會在 PeerFsm::handld_msgs
中收到該請求。
PeerFsm::propose_raft_command
PeerFsm 在會將該請求傳入 PeerFsm::propose_raft_command
作進一步處理。爲了突出重點,無關代碼已被刪去。
a. 檢查 store id,確認是否發送到發送到了對的 TiKV;
b. 檢查 peer id,確認是否發送到了對的 Peer;
c. 檢查 leadership,確認當前 Peer 是否爲 leader;
d. 檢查 Raft 任期,確認當前 leader 的任期是否符合請求中的要求;
e. 檢查 peer 初始化狀態,確認當前 Peer 已經初始化,有完整數據;
f. 檢查 region epoch,確認當前 Region 的 epoch 符合請求中的要求。
Peer::propose
因爲 RaftCmdRequest 可能包含了多種請求,加上請求間的處理方式各有不一樣,因此咱們須要判斷下該如何處理。
Peer::inspect
inspect 方法也不復雜,咱們住逐行看一下:
這判斷總的來講就是,若是不肯定能安全地讀 RocksDB 就用 read index,不然大膽地使用 local read 處理。
多線程 local read
細心的讀者可能已經發現,是否能 local read 關鍵在 leader 是否在 lease 內,而判斷 lease 實際上是不用通過 Raft 狀態機的,因此咱們能不能擴展下 lease,讓它能在多線程間共享,特別是在 TXN/MVCC 層,這樣讀請求就能繞過 Raft 直接執行了。答案是能夠的,並且 TiKV 已經實現了。話很少說,直接看代碼。
這個實現的有些取巧,咱們直接把它作到 raftstore 的入口處,也就是 RaftStoreRouter 中。這裏的 LocalReader 其實就是一個 cache,緩存了現有 leader 處理讀請求時的一些狀態。
LocalReader::execute_raft_command
上述代碼就是 Localreader 中處理請求的關鍵邏輯。注意爲了突出重點,咱們對該函數作了適當精簡,完整代碼請參考 連接。
Localreader 中對 lease 的處理和 raftstore 略有不一樣,關鍵代碼在 這裏 和 這裏,至於爲何能夠這麼寫,在這就不說了,做爲課後做業留給讀者思考 :-p
最後
read index 和 local read 的源碼閱讀就到這結束了,但願讀者看完後能瞭解並掌握 TiKV 處理讀請求的邏輯。