Linearizable Read通俗來講,就是讀請求需要讀到最新的已經(jīng)commit的數(shù)據(jù),不會讀到老數(shù)據(jù)。

對于使用raft協(xié)議來保證多副本強(qiáng)一致的系統(tǒng)中,讀寫請求都可以通過走一次raft協(xié)議來滿足。然后,現(xiàn)實(shí)系統(tǒng)中,讀請求通常會占很大比重,如果每次讀請求都要走一次raft落盤,性能可想而知。所以優(yōu)化讀性能至關(guān)重要。

從raft協(xié)議可知,leader擁有最新的狀態(tài),如果讀請求都走leader,那么leader可以直接返回結(jié)果給客戶端。然而,在出現(xiàn)網(wǎng)絡(luò)分區(qū)和時(shí)鐘快慢相差比較大的情況下,這有可能會返回老的數(shù)據(jù),即stale read,這違反了Linearizable Read。例如,leader和其他followers之間出現(xiàn)網(wǎng)絡(luò)分區(qū),其他followers已經(jīng)選出了新的leader,并且新的leader已經(jīng)commit了一堆數(shù)據(jù),然而由于不同機(jī)器的時(shí)鐘走的快慢不一,原來的leader可能并沒有發(fā)覺自己的lease過期,仍然認(rèn)為自己還是合法的leader直接給客戶端返回結(jié)果,從而導(dǎo)致了stale read。

Raft作者提出了一種叫做ReadIndex的方案:

當(dāng)leader接收到讀請求時(shí),將當(dāng)前commit index記錄下來,記作read index,在返回結(jié)果給客戶端之前,leader需要先確定自己到底還是不是真的leader,確定的方法就是給其他所有peers發(fā)送一次心跳,如果收到了多數(shù)派的響應(yīng),說明至少這個(gè)讀請求到達(dá)這個(gè)節(jié)點(diǎn)時(shí),這個(gè)節(jié)點(diǎn)仍然是leader,這時(shí)只需要等到commit index被apply到狀態(tài)機(jī)后,即可返回結(jié)果。

func (n *node) ReadIndex(ctx context.Context, rctx []byte) error {    return n.step(ctx, pb.Message{Type: pb.MsgReadIndex, Entries: []pb.Entry{{Data: rctx}}})
}

處理讀請求時(shí),應(yīng)用的goroutine會調(diào)用這個(gè)函數(shù),其中rctx參數(shù)相當(dāng)于讀請求id,全局保證唯一。step會往recvc中塞進(jìn)一個(gè)MsgReadIndex消息,而運(yùn)行node入口函數(shù)

        		

延伸閱讀

學(xué)習(xí)是年輕人改變自己的最好方式-Java培訓(xùn),做最負(fù)責(zé)任的教育,學(xué)習(xí)改變命運(yùn),軟件學(xué)習(xí),再就業(yè),大學(xué)生如何就業(yè),幫大學(xué)生找到好工作,lphotoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)學(xué)習(xí)是年輕人改變自己的最好方式