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ù)
延伸閱讀
- ssh框架 2016-09-30
- 阿里移動安全 [無線安全]玩轉(zhuǎn)無線電——不安全的藍(lán)牙鎖 2017-07-26
- 消息隊(duì)列NetMQ 原理分析4-Socket、Session、Option和Pipe 2024-03-26
- Selective Search for Object Recognition 論文筆記【圖片目標(biāo)分割】 2017-07-26
- 詞向量-LRWE模型-更好地識別反義詞同義詞 2017-07-26
- 從棧不平衡問題 理解 calling convention 2017-07-26
- php imagemagick 處理 圖片剪切、壓縮、合并、插入文本、背景色透明 2017-07-26
- Swift實(shí)現(xiàn)JSON轉(zhuǎn)Model - HandyJSON使用講解 2017-07-26
- 阿里移動安全 Android端惡意鎖屏勒索應(yīng)用分析 2017-07-26
- 集合結(jié)合數(shù)據(jù)結(jié)構(gòu)來看看(二) 2017-07-26