您好,登錄后才能下訂單哦!
這篇文章主要介紹“wal怎么實現日志讀寫”,在日常操作中,相信很多人在wal怎么實現日志讀寫問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”wal怎么實現日志讀寫”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
etcd raft是目前使用最廣泛的raft庫, etcd raft在etcd, Kubernetes, Docker Swarm, Cloud Foundry Diego, CockroachDB, TiDB, Project Calico, Flannel等分布式系統中都有應用,在生成環境得到了驗證。 傳統raft庫的實現都是單體設計(集成了存儲層、消息序列化、網絡層等), etcd raft繼承了簡約的設計理念,只實現了最核心的raft算法, 這樣更加的靈活。etcd將網絡、日志存儲、快照等功能分開,通過獨立的模塊實現,用戶可以在需要時調用。etcd自身實現了自己的一套raft配套庫:etcd-wal(用于存儲日志),snap(用于存儲快照),MemoryStorage(用于存儲當前日志、快照、狀態等信息以供raft核心程序使用)。
etcd wal介紹
WAL是write ahead log的縮寫,etcd使用wal模塊來完成raft日志的持久化存儲,etcd對wal的所有實現都放在wal目錄中。
type WAL struct {
lg *zap.Logger
dir string
// the living directory of the underlay files
// dirFile is a fd for the wal directory for syncing on Rename
dirFile *os.File
metadata []byte
// metadata recorded at the head of each WAL
state raftpb.HardState
// hardstate recorded at the head of WAL
start walpb.Snapshot
// snapshot to start reading
decoder *decoder
// decoder to decode records
readClose func() error
// closer for decode reader
mu sync.Mutex
enti uint64
// index of the last entry saved to the wal
encoder *encoder
// encoder to encode records
locks []*fileutil.LockedFile
// the locked files the WAL holds (the name is increasing)
fp *filePipeline
}
上述為wal的數據結構,通過用wal.go文件中的Create()方法來獲取wal的實例。wal首先會創建一個臨時目錄并初始化相關變量,并創建和初始化第一個wal文件,等所有的操作都初始化完成后直接更改臨時目錄的名字完成wal實例的初始化。
wal的所有日志放在一個指定目錄下,日志的文件名以 .wal 作為結尾,格式為-.wal,seq和index的格式都為%016x,例如:0000000000000001-0000000000000001.wal。index代表這個文件中第一條raft日志的index,seq是這個文件的序列號(依次遞增)。
每個文件的大小默認為64M,當文件大于64M時,wal會自動生成新的日志文件用于存儲日志。每個日志文件都會使用flock鎖定文件,參數為LOCK_EX,這是一把獨有鎖,同一時間只能有一個進程可以操作這個日志文件,所以當wal占有這個文件時,通過進程是無法刪除這個文件的。
wal日志可以存儲多種類型的數據,具體如下。
crcType 每個新的日志文件的第一條記錄都會是crcType類型的記錄,crcType也只會在每個日志文件的開始時寫入,用于記錄上一個文件最后的crc是多少
metadataType 每個新的日志文件中metadataType緊跟在crcType記錄后面,每個日志文件只會出現一次
stateType 這種日志類型會在兩種情況下加入:
自動切分日志文件時,新的日志文件中,緊跟在metadataType后面會存入一條stateType的日志
當raft核心程序ready中返回hard state時也會存儲該類型的日志
snapshotType wal日志中只會存儲snapshot的term和index,具體的數據存儲在專門的snapshot中,每次存儲快照都會在wal日志中存儲一個wal的快照。當存儲快照時,會將快照之前index的日志文件都釋放掉。wal中存儲的snapshot主要用于檢查快照是否正確。
wal通過封裝的encoder和decoder模塊來實現日志讀寫。
encoder模塊把會增量的計算crc和數據一起寫入到wal文件中。 下面為encoder數據結構
type encoder struct {
mu sync.Mutex
bw *ioutil.PageWriter
crc hash.Hash42
buf []byte
//緩存空間,默認為1M,降低數據分配的壓力
uint64buf []byte
}
wal通過encoder實現寫日志,在這個模塊中會完成crc的計算。wal為了更好的管理數據,日志中的每條數據都會以8字節對齊(wal會自動對齊字節)。 日志寫入的流程如下。
圖中的crc是增量計算,以之前的所有日志數據為增量基礎。 wal只關注寫入日志,不會校驗日志的index是否重復,但是如果重啟這個Node的話,系統會自動過濾掉中間混雜的日志。
wal實現了日志自動切分,當日志數據大于默認的64M時就會生成新的文件寫入日志,日志的切分通過wal.go文件中的cut方法來實現。cut方法只會在調用wal中的Save方法才會觸發調用,新文件的第一條記錄就是上一個wal文件最后的crc。
wal沒有實現日志的自動compact,系統只提供了MemoryStorage的日志compact方法(需要用戶主動調用)。
wal新建新的文件時都是先新建一個tmp文件,當所有操作都完成后再重命名這個文件。wal使用file_pipeline這個模塊在后臺啟動一個協程時刻準備一個臨時文件以供使用,從而避免臨時創建文件的開銷。
etcd raft自帶了go.etcd.io/etcd/etcdserver/api/snap模塊來實現快照的存儲。
在snap模塊中一個快照用一個后綴名為.snap的文件存儲,文件格式為-.snap, term和index分別代表快照日志所處的term和index。 每個快照具體存儲結構如下圖:
系統可以有多個快照,snap模塊使用Snapshotter結構統一管理快照。
type Snapshotter struct {
lg *zap.Logger
dir string
}
上面snapshotter的結構代碼,snapshotter主要用于存儲和讀取快照。
快照具體存儲的內容需要用戶來指定,例如在raft的官方例子中直接將當時的kv數據Marshal之后存儲到快照中。
func (s *kvstore) getSnapshot() ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return
json.Marshal(s.kvStore)
}
在etcd-raft中用戶可以選擇何時打快照,在etcd的官方案例中打快照的方法是maybeTriggerSnapshot(),這個方法在節點的Ready()方法返回時調用,當前提交的index值與上一次大快照的index值大于10000時會打新的快照。
MemoryStorage用于存儲raft節點臨時的數據,包括entrys、快照等。用戶將數據存儲到memoryStorage中,raft節點也會使用這些數據。包括entrys的傳遞、快照的發送等都是從memoryStorage中發送。
// MemoryStorage implements the Storage interface backed by an
// in-memory array.
type MemoryStorage struct {
// Protects access to all fields. Most methods of MemoryStorage are
// run on the raft goroutine, but Append() is run on an application
// goroutine.
sync.Mutex
hardState pb.HardState
snapshot pb.Snapshot
// ents[i] has raft log position i+snapshot.Metadata.Index
ents []pb.Entry
}
memoryStorage會存儲最新的entrys(包括哪些沒有commit)、快照和狀態,用戶在收到其它節點發送的相關數據時需要將數據存儲到memorystorage中。
到此,關于“wal怎么實現日志讀寫”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。