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

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

從raft協(xié)議可知,leader擁有最新的狀態(tài),如果讀請(qǐng)求都走leader,那么leader可以直接返回結(jié)果給客戶端。然而,在出現(xiàn)網(wǎng)絡(luò)分區(qū)和時(shí)鐘快慢相差比較大的情況下,這有可能會(huì)返回老的數(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接收到讀請(qǐng)求時(shí),將當(dāng)前commit index記錄下來,記作read index,在返回結(jié)果給客戶端之前,leader需要先確定自己到底還是不是真的leader,確定的方法就是給其他所有peers發(fā)送一次心跳,如果收到了多數(shù)派的響應(yīng),說明至少這個(gè)讀請(qǐng)求到達(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}}})
}

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

        		

網(wǎng)友評(píng)論